You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: vision-documents/variadic-generics.md
+47-3Lines changed: 47 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -106,6 +106,51 @@ map(1, "hello", true)
106
106
107
107
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))`.
108
108
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 argumentlabels:
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}` likethis:
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 optionalelements:
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
+
109
154
#### Static shape of a parameter pack
110
155
111
156
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
135
180
136
181
```swift
137
182
struct List<Element...> {
138
-
let elements: (Element...)
139
-
init(_ element: Element) { elements = (element...) }
0 commit comments