Skip to content

Commit de4b6a2

Browse files
committed
SE-0393 revisions.
1 parent 039063a commit de4b6a2

File tree

1 file changed

+35
-12
lines changed

1 file changed

+35
-12
lines changed

proposals/0393-parameter-packs.md

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ This proposal adds _type parameter packs_ and _value parameter packs_ to enable
5353
- [Value expansion operator](#value-expansion-operator)
5454
- [Pack destructuring operations](#pack-destructuring-operations)
5555
- [Tuple conformances](#tuple-conformances)
56+
- [Revision history](#revision-history)
5657
- [Acknowledgments](#acknowledgments)
5758

5859
## Motivation
@@ -99,7 +100,7 @@ A pack expansion consists of the `repeat` keyword followed by a type or an expre
99100
Similarly, pack references can only appear inside repetition patterns and generic requirements:
100101

101102
```swift
102-
func zip<each S>(_ sequence: repeat each S) where each S: Sequence
103+
func zip<each S>(_ sequence: repeat each S) where repeat each S: Sequence
103104
```
104105

105106
Given a concrete pack substitution, the pattern is repeated for each element in the substituted pack. If `S` is substituted with `Array<Int>, Set<String>`, then `repeat Optional<each S>` will repeat the pattern `Optional<each S>` for each element in the substitution to produce `Optional<Array<Int>>, Optional<Set<String>>`.
@@ -384,20 +385,20 @@ We will refer to `each T` as the _root type parameter pack_ of the member type p
384385

385386
### Generic requirements
386387

387-
All existing kinds of generic requirements generalize to type parameter packs. Same-type requirements generalize in multiple different ways, depending on whether one or both sides involve a type parameter pack.
388+
All existing kinds of generic requirements can be used inside _requirement expansions_, which represent a list of zero or more requirements. Requirement expansions are spelled with the `repeat` keyword followed by a generic requirement pattern that captures at least one type parameter pack reference spelled with the `each` keyword. Same-type requirements generalize in multiple different ways, depending on whether one or both sides involve a type parameter pack.
388389

389390
1. Conformance, superclass, and layout requirements where the subject type is a type parameter pack are interpreted as constraining each element of the replacement type pack:
390391

391392
```swift
392-
func variadic<each S>(_: repeat each S) where each S: Sequence { ... }
393+
func variadic<each S>(_: repeat each S) where repeat each S: Sequence { ... }
393394
```
394395

395396
A valid substitution for the above might replace `S` with `{Array<Int>, Set<String>}`. Expanding the substitution into the requirement `each S: Sequence` conceptually produces the following conformance requirements: `Array<Int>: Sequence, Set<String>: Sequence`.
396397

397-
2. A same-type requirement where one side is a type parameter pack and the other type is a scalar type that does not capture any type parameter packs is interpreted as constraining each element of the replacement type pack to _the same_ scalar type:
398+
1. A same-type requirement where one side is a type parameter pack and the other type is a scalar type that does not capture any type parameter packs is interpreted as constraining each element of the replacement type pack to _the same_ scalar type:
398399

399400
```swift
400-
func variadic<each S: Sequence, T>(_: repeat each S) where (each S).Element == T {}
401+
func variadic<each S: Sequence, T>(_: repeat each S) where repeat (each S).Element == T {}
401402
```
402403

403404
This is called a _same-element requirement_.
@@ -408,7 +409,7 @@ All existing kinds of generic requirements generalize to type parameter packs. S
408409
3. A same-type requirement where each side is a pattern type that captures at least one type parameter pack is interpreted as expanding the type packs on each side of the requirement, equating each element pair-wise.
409410

410411
```swift
411-
func variadic<each S: Sequence, each T>(_: repeat each S) where (each S).Element == Array<each T> {}
412+
func variadic<each S: Sequence, each T>(_: repeat each S) where repeat (each S).Element == Array<each T> {}
412413
```
413414

414415
This is called a _same-type-pack requirement_.
@@ -560,16 +561,30 @@ func overload<T>(_: T) {}
560561
func overload<each T>(_: repeat each T) {}
561562
```
562563

563-
If both overloads match a given call, e.g. `overload(1)`, the call is ambiguous. Similarly, two generic functions where one accepts a non-pack variadic parameter and the other accepts a type parameter pack:
564+
If the parameters of the scalar overload have the same or refined requirements as the parameter pack overload, the scalar overload is considered a subtype of the parameter pack overload, because the parameters of the scalar overload can be forwarded to the parameter pack overload. Generally, if a function call successfully type checks with two different overloads, the subtype is preferred. This effectively means that scalar overloads are preferred over parameter pack overloads when the scalar requirements meet the requirements of the parameter pack:
564565

565566
```swift
566-
func overload<T>(_: T...) {}
567+
func overload() {}
568+
func overload<T>(_: T) {}
567569
func overload<each T>(_: repeat each T) {}
568570

569-
overload() // ambiguity error
571+
overload() // calls the no-parameter overload
572+
573+
overload(1) // calls the scalar overload
574+
575+
overload(1, "") // calls the parameter pack overload
570576
```
571577

572-
In other words, variadic generic functions have the same ranking as other generic functions.
578+
The general overload subtype ranking rule applies after localized ranking, such as implicit conversions and optional promotions. That remains unchanged with this proposal. For example:
579+
580+
```swift
581+
func overload<T>(_: T, _: Any) {}
582+
func overload<each T>(_: repeat each T) {}
583+
584+
overload(1, "") // prefers the parameter pack overload because the scalar overload would require an existential conversion
585+
```
586+
587+
This overload resolution behavior enables library authors to introduce new function overloads using parameter packs that generalize existing fixed-arity overloads while preserving the overload resolution behavior of existing code.
573588

574589
## Effect on ABI stability
575590

@@ -697,7 +712,7 @@ In this proposal, type packs do not have an explicit syntax, and a type pack is
697712
```swift
698713
struct Variadic<each T> {}
699714

700-
extension Variadic where each T == {Int, String} {} // {Int, String} is a concrete pack
715+
extension Variadic where T == {Int, String} {} // {Int, String} is a concrete pack
701716
```
702717

703718
### Pack iteration
@@ -727,7 +742,7 @@ Use cases for variadic generics that break up pack iteration across function cal
727742
Dynamic pack indexing is useful when the specific type of the element is not known, or when all indices must have the same type, such as for index manipulation or storing an index value. Packs could support subscript calls with an `Int` index, which would return the dynamic type of the pack element directly as the opened underlying type that can be assigned to a local variable with opaque type. Values of this type need to be erased or cast to another type to return an element value from the function:
728743

729744
```swift
730-
func element<each T>(at index: Int, in t: repeat each T) where each T: P -> any P {
745+
func element<each T: P>(at index: Int, in t: repeat each T) -> any P {
731746
// The subscript returns 'some P', which is erased to 'any P'
732747
// based on the function return type.
733748
let value: some P = t[index]
@@ -831,6 +846,14 @@ extension<each T: Equatable> (repeat each T): Equatable {
831846
}
832847
```
833848

849+
## Revision history
850+
851+
Changes to the [first reviewed revision](https://github.com/apple/swift-evolution/blob/b6ca38b9eee79650dce925e7aa8443a6a9e5e6ea/proposals/0393-parameter-packs.md):
852+
853+
* The `repeat` keyword is required for generic requirement expansions to distinguish requirement expansions from single requirements on an individual pack element nested inside of a pack expansion expression.
854+
* Overload resolution prefers scalar overloads when the scalar overload is considered a subtype of a parmeter pack overload.
855+
856+
834857
## Acknowledgments
835858

836859
Thank you to Robert Widmann for exploring the design space of modeling packs as tuples, and to everyone who participated in earlier design discussions about variadic generics in Swift. Thank you to the many engineers who contributed to the implementation, including Sophia Poirier, Pavel Yaskevich, Nate Chandler, Hamish Knight, and Adrian Prantl.

0 commit comments

Comments
 (0)