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
-[Restrictions on same-shape requirements](#restrictions-on-same-shape-requirements)
29
29
-[Value parameter packs](#value-parameter-packs)
30
30
-[Local value packs](#local-value-packs)
31
31
-[Effect on ABI stability](#effect-on-abi-stability)
@@ -87,7 +87,7 @@ func zip<each S: Sequence>(...)
87
87
88
88
A parameter pack itself is not a first-class value or type, but the elements of a parameter pack can be used anywhere that naturally accepts a comma-separated list of values or types using _pack expansions_. A pack expansion unpacks the elements of a pack into a comma-separated list, and elements can be appended to either side of a pack expansion by writing more values in the comma-separated list.
89
89
90
-
A pack expansion consists of the `repeat` keyword followed by a type or an expression. The type or expression that `repeat` is applied to is called the _repetition pattern_. The repetition pattern must contain pack references. Similarly, pack references can only appear inside reptition patterns and generic requirements:
90
+
A pack expansion consists of the `repeat` keyword followed by a type or an expression. The type or expression that `repeat` is applied to is called the _repetition pattern_. The repetition pattern must contain pack references. Similarly, pack references can only appear inside repetition patterns and generic requirements:
@@ -167,11 +167,11 @@ The restriction where only unlabeled elements of a tuple type may have a pack ex
167
167
168
168
The captures of the pattern type are a subset of the captures of the pack expansion type itself. In some situations (described in the next section), the pack expansion type might capture a type parameter pack that does not appear in the pattern type.
169
169
170
-
**Typing rules:** A pack expansion type is _well-typed_ if the pattern type would be well-typed if the captured type parameter packs were replaced by references to scalar type parameters with the same constraints.
170
+
**Typing rules:** A pack expansion type is _well-typed_ if replacing the captured type parameter packs in the pattern type with scalar type parameters of the same constraints produces a well-typed scalar type.
171
171
172
-
For example, if `T` is a type parameter pack subject to the conformance requirement `each T: Hashable`, then `repeat Set<each T>` is well-typed.
172
+
For example, if `each T` is a type parameter pack subject to the conformance requirement `each T: Hashable`, then `repeat Set<each T>` is well-typed, because `Set<T>` is well-typed given `T: Hashable`.
173
173
174
-
However, if `T` were not subject to this conformance requirement, then `repeat Set<each T>` would not be well-typed; the user might substitute `T` with a type pack containing types that do not conform to `Hashable`, like `T := {AnyObject, Int}`, and the substituted type sequence `Set<AnyObject>, Set<Int>` is not well-typed because `Set<AnyObject>` is not well-typed.
174
+
However, if `each T` were not subject to this conformance requirement, then `repeat Set<each T>` would not be well-typed; the user might substitute `T` with a type pack containing types that do not conform to `Hashable`, like `T := {AnyObject, Int}`, and the substituted type sequence `Set<AnyObject>, Set<Int>` is not well-typed because `Set<AnyObject>` is not well-typed.
175
175
176
176
### Type substitution
177
177
@@ -296,7 +296,7 @@ Given a function declaration that is well-formed under this rule, type matching
296
296
297
297
#### Type sequence matching
298
298
299
-
In all other cases, we're matching two type sequences. If either type sequence contains two or more pack expansion types, the match remains _unsolved_, and the type checker attempts to derive substitutions by matching other types before giving up. (This allows a call to `concat()` as defined above to succeed, for example; the match between the contextual return type and `(repeaceach T, repeateach U)` remains unsolved, but we are able to derive the substitutions for `T` and `U` from the call argument expressions.)
299
+
In all other cases, we're matching two type sequences. If either type sequence contains two or more pack expansion types, the match remains _unsolved_, and the type checker attempts to derive substitutions by matching other types before giving up. (This allows a call to `concat()` as defined above to succeed, for example; the match between the contextual return type and `(repeateach T, repeateach U)` remains unsolved, but we are able to derive the substitutions for `T` and `U` from the call argument expressions.)
300
300
301
301
Otherwise, we match the common prefix and suffix as long as no pack expansion types appear on either side. After this has been done, there are three possibilities:
302
302
@@ -371,7 +371,7 @@ All existing kinds of generic requirements generalize to type parameter packs. S
371
371
2. A same-type requirement where one side is a type parameter pack and the other type is a concrete type that does not capture any type parameter packs is interpreted as constraining each element of the replacement type pack to _the same_ concrete type:
372
372
373
373
```swift
374
-
func variadic<each S:Sequence, T>(_: repeateach S) where (each S).Element==Array<T> {}
374
+
func variadic<each S:Sequence, T>(_: repeateach S) where (each S).Element==T {}
375
375
```
376
376
377
377
This is called a _concrete same-element requirement_.
@@ -459,17 +459,17 @@ func foo<each T, each U>(t: repeat each T, u: repeat each U) {
459
459
}
460
460
```
461
461
462
-
The type annotation of `tup` contains a pack expansion type `repeat (each T, each U)`, which is malformed because the requirement `count(T) ==count(U)` is unsatisfied. This pack expansion type is not subject to requirement inference because it does not occur in one of the above positions.
462
+
The type annotation of `tup` contains a pack expansion type `repeat (each T, each U)`, which is malformed because the requirement `shape(T) ==shape(U)` is unsatisfied. This pack expansion type is not subject to requirement inference because it does not occur in one of the above positions.
463
463
464
-
#### Open questions
464
+
#### Restrictions on same-shape requirements
465
465
466
466
While type packs cannot be written directly, a requirement where both sides are concrete types is desugared using the type matching algorithm, therefore it will be possible to write down a requirement that constrains a type parameter pack to a concrete type pack, unless some kind of restriction is imposed:
Furthermore, since the same-type requirement implies a same-shape requirement, we've actually implicitly constrained `T` to having a length of 2 elements, without knowing what those elements are.
472
+
Furthermore, since the same-type requirement implies a same-shape requirement, we've implicitly constrained `T` to having a length of 2 elements, without knowing what those elements are.
473
473
474
474
This introduces theoretical complications. In the general case, same-type requirements on type parameter packs allows encoding arbitrary systems of integer linear equations:
475
475
@@ -483,15 +483,17 @@ func solve<each Q, each R, each S>(q: repeat each Q, r: repeat each R, s: repeat
483
483
484
484
While type-level linear algebra is interesting, we may not ever want to allow this in the language to avoid significant implementation complexity, and we definitely want to disallow this expressivity in this proposal.
485
485
486
-
However, how to impose restrictions on same-shape and same-type requirementsis an open question. One possibility is to disallow these requirements entirely, but doing so would likely be too limiting. Another possibility is to formalize the concept of the structure or “shape” of a pack, where a shape is one of:
486
+
To impose restrictions on same-shape and same-type requirements, we will formalize the concept of the “shape” of a pack, where a shape is one of:
487
487
488
488
* A single scalar type element; all scalar types have a singleton ``scalar shape''
489
489
* An abstract shape that is specific to a pack parameter
490
490
* A concrete shape that is composed of the scalar shape and abstract shapes
491
491
492
-
For example, the pack `{Int, repeateach T, U}` has a concrete shape that consists of two single elements and one abstract shape. We could impose restrictions where packs that are unified together must have the same shape, which may reduce the problem to “shape equivalence classes” rather than an arbitrary system of linear equations. Giving packs a statically known structure may also be useful for destructuring packs in generic contexts, which is a possible future direction.
492
+
For example, the pack `{Int, repeateach T, U}` has a concrete shape that consists of two single elements and one abstract shape.
493
+
494
+
This proposal only enables abstract shapes. Each type parameter pack has an abstract shape, and same-shape requirements merge equivalence classes of abstract shapes. Any same-type requirement that imposes a concrete shape on a type parameter pack will be diagnosed as a *conflict*, much like other conflicting requirements such as `where T ==Int, T ==String` today.
493
495
494
-
This aspect of the language can evolve in a forward-compatible manner. To begin with, we can start with the simplest form of same-shape requirements, whereeach type parameter pack has an abstract shape, and same-shape requirements merge equivalence classes of abstract shapes. Any attempt to define a same-shape requirement involving a concrete type can be diagnosed as a *conflict*, much like we reject conflicting requirements such as `where T ==Int, T ==String` today. Over time, some restrictions can be lifted, while others remain, as different use-cases for type parameter packs are revealed.
496
+
This aspect of the language can evolve in a forward-compatible manner. Over time, some restrictions can be lifted, while others remain, as different use-cases for type parameter packs are revealed.
A previous version of this proposal used `...` as the pack expansion operator with no explicit syntax for pack elements in pattern types. This syntax choice follows precedence from C++ variadic templates and non-pack variadic parameters in Swift. However, there are some serious downsides of this choice, because ...is already a postfix unary operatorin the Swift standard library that is commonly used across existing Swift code bases, which lead to the following ambiguities:
585
+
A previous version of this proposal used `...` as the pack expansion operator with no explicit syntax for pack elements in pattern types. This syntax choice follows precedent from C++ variadic templates and non-pack variadic parameters in Swift. However, there are some serious downsides of this choice, because ...is already a postfix unary operatorin the Swift standard library that is commonly used across existing Swift code bases, which lead to the following ambiguities:
584
586
585
587
1. **Pack expansion vs non-pack variadic parameter.** Using `...` for pack expansions in parameter lists introduces an ambiguity with the use of `...` to indicate a non-pack variadic parameter. This ambiguity can arise when expanding a type parameter pack into the parameter list of a function type. For example:
586
588
@@ -621,7 +623,7 @@ In the above code, `values...` in the expansion pattern could mean either:
621
623
let foo = Foo<T...>()
622
624
```
623
625
624
-
Here, the ambiguous parse is with the token `...>`. We propose changing the grammar so that `...>` is no longer considered as a single token, and instead parses as the token `...` followed by the token `>`.
626
+
Here, the ambiguous parse is with the token `...>`, which would necessitate changing the grammar so that `...>` is no longer considered as a single token, and instead parses as the token `...` followed by the token `>`.
625
627
626
628
627
629
#### Another operator
@@ -640,11 +642,6 @@ The downsides to postfix `*` include:
640
642
* `*` evokes pointer types / a dereferencing operator to programmers familiar with other languages including C/C++, Go, Rust, etc.
641
643
* Choosing another operator does not alleviate the ambiguities in expressions, because values could also have a postfix `*` operator or any other operator symbol, leading to the same ambiguity.
642
644
643
-
The downsides to introducing keywords are:
644
-
645
-
* Though the keywords are more verbose than an operator, using the `expand` keyword in expression context is still fairly subtle because it looks just like a function call rather than a built in expansion operation.
646
-
* Introducing a new keyword in expression context would break existing code that uses that keyword name, e.g. as the name of a function
647
-
648
645
#### Magic builtin `map` method
649
646
650
647
The prevlence of `map` and `zip` in Swift makes this syntax an attractive option for variadic generics:
0 commit comments