Skip to content

Commit 69c77fa

Browse files
committed
Add a note on parameter pack naming convention.
1 parent 4d12363 commit 69c77fa

File tree

1 file changed

+47
-3
lines changed

1 file changed

+47
-3
lines changed

vision-documents/variadic-generics.md

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,51 @@ map(1, "hello", true)
106106

107107
In the above code, the call to the variadic `map` function infers the type parameter pack substitution `T:= {Int, String, Bool}` from the argument values. Expanding the repetition pattern `Mapped<T>` into a tuple type produces a tuple return type of `(Mapped<Int>, Mapped<String>, Mapped<Bool>)`. Substituting the value parameter pack with `t := {1, "hello", true}` and expanding the repetition pattern `Mapped(t)` into a tuple value produces a tuple return value `(Mapped(1), Mapped("hello"), Mapped(true))`.
108108

109+
#### A note on naming convention
110+
111+
In code, programmers naturally use plural names for variables representing lists. However, in this design for variadic generics, pack names can only appear in repetition patterns where the type or expression represents an individual type or value that is repeated under expansion. The recommended naming convention is to use singular names for parameter packs, and plural names only in argument labels:
112+
113+
```swift
114+
struct List<Element...> {
115+
let element: Element...
116+
init(elements element: Element...)
117+
}
118+
119+
List(elements: 1, "hello", true)
120+
```
121+
122+
More broadly, packs are fundamentally different from first-class types and values in Swift. Packs themselves are not types or values; they are a special kind of entity that allow you to write one piece of code that is repeated for *N* individual types or values. For example, consider the `element` stored property pack in the `List` type:
123+
124+
```swift
125+
struct List<Element...> {
126+
let element: Element...
127+
}
128+
```
129+
130+
The way to think about the property pack `let element: Element...` is "a property called `element` with type `Element`, repeated *N* times". When `List` is initialized with 3 concrete arguments, e.g. `List<Int, String, Bool>`, the stored property pack expands into 3 respective stored properties of type `Int`, `String`, and `Bool`. You might conceptualize `List` specialization for `{Int, String, Bool}` like this:
131+
132+
```swift
133+
struct List<Int, String, Bool> {
134+
let element.0: Int
135+
let element.1: String
136+
let element.2: Bool
137+
}
138+
```
139+
140+
The singular nature of pack names becomes more evident with more sophisticated repetition patterns. Consider the following `withOptionalElements` method, which returns a new list containing optional elements:
141+
142+
```swift
143+
extension List {
144+
func withOptionalElements() -> List<Optional<Element>...> {
145+
return List(elements: Optional(element)...)
146+
}
147+
}
148+
```
149+
150+
In the return type, the repetition pattern `Optional<Element>...` means there is an optional type for each individual element in the parameter pack. When this method is called on `List<Int, String, Bool>`, the pattern is repeated once for each individual type in the list, replacing the reference to `Element` with that individual type, resulting in `Optional<Int>, Optional<String>, Optional<Bool>`.
151+
152+
The singular naming convention encourages this model of thinking about parameter packs.
153+
109154
#### Static shape of a parameter pack
110155

111156
Validating variadic generic code separately from its application requires introducing the notion of abstract length of a list of type parameters into the type system. Operations over parallel lists, such as statically zipping two separate lists of type parameters to create a new list of 2-element tuple types, require that multiple lists have the length.
@@ -135,13 +180,12 @@ The statically-known shape of a pack can enable destructing packs with concrete
135180

136181
```swift
137182
struct List<Element...> {
138-
let elements: (Element...)
139-
init(_ element: Element) { elements = (element...) }
183+
let element: Element...
140184
}
141185

142186
extension List {
143187
func firstRemoved<First, Rest...>() -> List<Rest...> where (Element...) == (First, Rest...) {
144-
let (first, rest) = (value...)
188+
let (first, rest) = (element...)
145189
return List(rest...)
146190
}
147191
}

0 commit comments

Comments
 (0)