|
37 | 37 | - [Magic builtin `map` method](#magic-builtin-map-method)
|
38 | 38 | - [Future directions](#future-directions)
|
39 | 39 | - [Variadic generic types](#variadic-generic-types)
|
| 40 | + - [Pack iteration](#pack-iteration) |
| 41 | + - [Pack element projection](#pack-element-projection) |
| 42 | + - [Dynamic pack indexing with `Int`](#dynamic-pack-indexing-with-int) |
| 43 | + - [Typed pack element projection using key-paths](#typed-pack-element-projection-using-key-paths) |
40 | 44 | - [Value expansion operator](#value-expansion-operator)
|
41 | 45 | - [Pack destructuring operations](#pack-destructuring-operations)
|
42 | 46 | - [Tuple conformances](#tuple-conformances)
|
@@ -666,6 +670,57 @@ The downsides of a magic `map` method are:
|
666 | 670 |
|
667 | 671 | This proposal only supports type parameter packs on functions. A complementary proposal will describe type parameter packs on generic structs, enums and classes.
|
668 | 672 |
|
| 673 | +### Pack iteration |
| 674 | + |
| 675 | +All list operations can be expressed using pack expansion expressions by factoring code involving statements into a function or closure. However, this approach does not allow for short-circuiting, because the pattern expression will always be evaluated once for every element in the pack. Further, requiring a function or closure for code involving statements is unnatural. Allowing `for-in` loops to iterate over packs solves both of these problems. |
| 676 | + |
| 677 | +Value packs could be expanded into the source of a `for-in` loop, allowing you to iterate over each element in the pack and bind each value to a local variable: |
| 678 | + |
| 679 | +```swift |
| 680 | +func allEmpty<each T>(_ array: repeat [each T]) -> Bool { |
| 681 | + for a in repeat each array { |
| 682 | + guard a.isEmpty else { return false } |
| 683 | + } |
| 684 | + |
| 685 | + return true |
| 686 | +} |
| 687 | +``` |
| 688 | + |
| 689 | +The type of the local variable `a` in the above example is an `Array` of an opaque element type with the requirements that are written on `each T`. For the *i*th iteration, the element type is the *i*th type parameter in the type parameter pack `T`. |
| 690 | + |
| 691 | +### Pack element projection |
| 692 | + |
| 693 | +Use cases for variadic generics that break up pack iteration across function calls, require random access, or operate over concrete packs can be supported in the future by projecting individual elements out from a parameter pack. Because elements of the pack have different types, there are two approaches to pack element projection; using an `Int` index which will return the dynamic type of the element, and using a statically typed index which is parameterized over the requested pack element type. |
| 694 | + |
| 695 | +#### Dynamic pack indexing with `Int` |
| 696 | + |
| 697 | +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: |
| 698 | + |
| 699 | +```swift |
| 700 | +func element<each T>(at index: Int, in t: repeat each T) where each T: P -> any P { |
| 701 | + // The subscript returns 'some P', which is erased to 'any P' |
| 702 | + // based on the function return type. |
| 703 | + let value: some P = t[index] |
| 704 | + return value |
| 705 | +} |
| 706 | +``` |
| 707 | + |
| 708 | +#### Typed pack element projection using key-paths |
| 709 | + |
| 710 | +Some use cases for pack element projection know upfront which type within the pack will be projected, and can use a statically typed pack index. A statically typed pack index could be represented with `KeyPath` or a new `PackIndex` type, which is parameterized over the base type for access (i.e. the pack), and the resulting value type (i.e. the element within the pack to project). Pack element projection via key-paths falls out of 1) positional tuple key-paths, and 2) expanding packs into tuple values: |
| 711 | + |
| 712 | +```swift |
| 713 | +struct Tuple<each Elements> { |
| 714 | + var elements: (repeat each Elements) |
| 715 | + |
| 716 | + subscript<Value>(keyPath: KeyPath<(repeat each Elements), Value>) -> Value { |
| 717 | + return elements[keyPath: keyPath] |
| 718 | + } |
| 719 | +} |
| 720 | +``` |
| 721 | + |
| 722 | +The same positional key-path application could be supported directly on value packs. |
| 723 | + |
669 | 724 | ### Value expansion operator
|
670 | 725 |
|
671 | 726 | This proposal only supports the expansion operator on type parameter packs and value parameter packs, but there are other values that represent a list of zero or more values that the expansion operator would be useful for, including tuples and arrays. It would be desirable to introduce a new kind of expression that receives a scalar value and produces a value of pack expansion type.
|
|
0 commit comments