From 377d6bc537431af88a126cbaef30b4f760510d24 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 4 Apr 2023 17:04:35 -0700 Subject: [PATCH 01/14] Sketch additions to the formal grammar. --- TSPL.docc/ReferenceManual/Expressions.md | 8 ++++++++ TSPL.docc/ReferenceManual/Types.md | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 1e44658ab..28e141fda 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -1523,6 +1523,14 @@ A single expression inside parentheses is a parenthesized expression. > > *tuple-element* → *expression* | *identifier* **`:`** *expression* +### Parameter Pack Expression + +> Grammar of a pack-expansion expression: +> +> *parameter-pack-expression* → **`each`** *expression* +> +> *parameter-pack-expansion-expression* → **`repeat`** *expression* + ### Wildcard Expression A *wildcard expression* diff --git a/TSPL.docc/ReferenceManual/Types.md b/TSPL.docc/ReferenceManual/Types.md index cc81e8c03..f733c772b 100644 --- a/TSPL.docc/ReferenceManual/Types.md +++ b/TSPL.docc/ReferenceManual/Types.md @@ -54,6 +54,8 @@ and describes the type inference behavior of Swift. > > *type* → *opaque-type* > +> *type* → *parameter-pack-type* +> > *type* → *metatype-type* > > *type* → *any-type* @@ -895,6 +897,20 @@ could return a value of type `T` or `Dictionary`. > > *opaque-type* → **`some`** *type* +## Parameter Pack Type + +XXX OUTLINE: + +- `each T` creates a parameter pack type +- `repeat T` expands the parameter pack type +- To expand the values, you use a parameter pack expression (xref) + +> Grammar of a parameter pack type: +> +> *parameter-pack-type* → **`each`** *type* +> +> *parameter-pack-expansion-type* → **`repeat`** *type* + ## Metatype Type A *metatype type* refers to the type of any type, From 4c46c9c00ec4ad67361706fb3a701df9f023ff54 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 7 Apr 2023 16:55:12 -0700 Subject: [PATCH 02/14] Start outlining the guide and reference for packs. --- TSPL.docc/LanguageGuide/Generics.md | 111 +++++++++++++++++++++++ TSPL.docc/ReferenceManual/Expressions.md | 18 ++++ TSPL.docc/ReferenceManual/Types.md | 22 ++++- 3 files changed, 150 insertions(+), 1 deletion(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index 5e5e9d417..88f79b636 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -1853,6 +1853,117 @@ protocol ComparableContainer: Container where Item: Comparable { } } --> +## Generic Parameter Packs + +XXX OUTLINE: + +Why parameter packs? + +- Another way that code can be generic + is to accept and return a list of values + where the length of that list can vary. + +- If you want to preserve type information, + you could manually provide overloads + that take a different number of arguments: + + ```swift + func f(_ x1: Int) -> Int { ... } + func f(_ x1: Int, _ x2: Int) -> Int { ... } + func f(_ x1: Int, _ x2: Int, _ x3: Int) -> Int { ... } + ``` + + FIXME: This kind of example won't work, + because you can't iterate over the elements of a pack + to combine them in some way into a single result. + Need to frame the examples around "structural" code. + + But that's tedious and repetitive, + and still imposes an arbitrary upper limit. + +- You could do this with type erasure: + + ```swift + func f(xs x: [Any]) -> [Any] { ... } + ``` + +- Parameter packs let have both -- + a function that can take a variable number of parameters, + while still preserving type information. + +How do you create a parameter pack? + +- In the generic type parameters, + write `each` in front of the type that can occur multiple times. + +- In the function's parameters, + write `repeat` in front of the type for the parameter + that can accept a variable number of arguments. + +- Inside the pack-expansion type, + write `each` in front of every place where the repetition takes place. + In the simple case, where only one type repeats, + this means you write `repeat each T` or similar. + +- Naming convention: + Use singular names for parameter packs, + and plural names only in argument labels. + +What else can you repeat? +How do you repeat more than one type? + +- For collections or other generic types, + the pack expansion can happen inside, + like `repeat Array` expands to multiple array types. + +- A more complex type can include `each` multiple times, + like `repeat Dictionary`. + All of the expansions have to be the same size --- + in this example, + the list of types that `Key` expands to must be the same length + as the list that `Value` expands to. + +How do you access the values of a parameter pack? + +- Inside the function body, you use `repeat` + to make the places where code must be expanded. + +- When you use `repeat` at the start of a line (as a statement), + the whole line is duplicated once for each type. + When you use `repeat` in the middle of a line (as an expression), + it expands to make a tuple + with one tuple element for each type. + +- After `repeat`, you write `each` in front of the type being expanded. + You can expand multiple packs in the same repeat expression, + as long as all of the packs have the same length. + + ```swift + repeat print(each t) + return (Pair(each first, each second)) + ``` + +- Tripping hazard: + You don't always write `repeat each` one after the other. + The part to repeat is marked `repeat` + and the location of the element from the pack is marked `each`. + + For example, + `repeat (each T, Int)` is different from `(repeat each T, Int)`. + The former makes multiple tuple `(T1, Int) ... (Tn, Int)`, + expanding `T` and adding `Int` to each tuple. + The latter makes one tuple, `(T1, ..., Tn, Int)`. + Other code can come between `repeat` and `each` --- + both of those are different from `repeat (Int, each T)` + +- You can expand a parameter pack's values + only inside a tuple or a function call. + (TR: Also inside arguments to a macro? + Doug brought that up during the SE review.) + A notable omission is that + there isn't a way to iterate over the values in a pack --- + the SE proposal calls that out as a potential future direction. + ## Generic Subscripts Subscripts can be generic, diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 28e141fda..e84526c57 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -1525,6 +1525,24 @@ A single expression inside parentheses is a parenthesized expression. ### Parameter Pack Expression +XXX OUTLINE: + +- a `repeat` expression must contain one or more `each` expressions + and has the shape `repeat <#repetition pattern#>` + +- TODO list of contexts where expansion is supported + + + in a tuple, producing tuple elements + + as a statement, repeating the statement's expression + + but not in a function call, producing arguments + +- The *repetition pattern* is repeated once for each type in the pack + +- all of the `each` expressions must expand packs with the same number of types + +- It's valid for a pack expression contain no elements, + in which case the parameter-pack expansion expression isn't evaluated at all (zero times) + > Grammar of a pack-expansion expression: > > *parameter-pack-expression* → **`each`** *expression* diff --git a/TSPL.docc/ReferenceManual/Types.md b/TSPL.docc/ReferenceManual/Types.md index f733c772b..799d308f4 100644 --- a/TSPL.docc/ReferenceManual/Types.md +++ b/TSPL.docc/ReferenceManual/Types.md @@ -902,8 +902,18 @@ could return a value of type `T` or `Dictionary`. XXX OUTLINE: - `each T` creates a parameter pack type + when it appears in a generic parameter clause, + or indicates which pack should be expanded + when it appears in a `repeat` expression + - `repeat T` expands the parameter pack type -- To expand the values, you use a parameter pack expression (xref) + +- Packs are never nested; expansion implies flattening + +- To expand the values, you use a parameter pack expression; + see + +- It's valid for a pack type to contain no elements. > Grammar of a parameter pack type: > @@ -911,6 +921,16 @@ XXX OUTLINE: > > *parameter-pack-expansion-type* → **`repeat`** *type* + + ## Metatype Type A *metatype type* refers to the type of any type, From 5201f45a92ba73ec74bfb02c57e39f4b316a73ca Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 1 May 2023 11:19:29 -0700 Subject: [PATCH 03/14] Adjust terminology. --- TSPL.docc/ReferenceManual/Expressions.md | 2 +- TSPL.docc/ReferenceManual/Types.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index e84526c57..3fc2fb273 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -1533,7 +1533,7 @@ XXX OUTLINE: - TODO list of contexts where expansion is supported + in a tuple, producing tuple elements - + as a statement, repeating the statement's expression + + as a statement, including at top level, repeating the statement's expression + but not in a function call, producing arguments - The *repetition pattern* is repeated once for each type in the pack diff --git a/TSPL.docc/ReferenceManual/Types.md b/TSPL.docc/ReferenceManual/Types.md index 799d308f4..6ccb0b37d 100644 --- a/TSPL.docc/ReferenceManual/Types.md +++ b/TSPL.docc/ReferenceManual/Types.md @@ -897,29 +897,29 @@ could return a value of type `T` or `Dictionary`. > > *opaque-type* → **`some`** *type* -## Parameter Pack Type +## Type-Parameter Pack XXX OUTLINE: -- `each T` creates a parameter pack type +- `each T` creates a type-parameter pack when it appears in a generic parameter clause, or indicates which pack should be expanded when it appears in a `repeat` expression -- `repeat T` expands the parameter pack type +- `repeat T` expands the type-parameter pack - Packs are never nested; expansion implies flattening - To expand the values, you use a parameter pack expression; see -- It's valid for a pack type to contain no elements. +- It's valid for a type pack to contain no elements. -> Grammar of a parameter pack type: +> Grammar of a type-parameter pack: > -> *parameter-pack-type* → **`each`** *type* +> *type-parameter-pack* → **`each`** *type* > -> *parameter-pack-expansion-type* → **`repeat`** *type* +> *type-parameter-pack-expansion* → **`repeat`** *type* + + + +XXX OUTLINE: + +Why parameter packs? -- Parameter packs let have both -- - a function that can take a variable number of parameters, - while still preserving type information. +- Another way that code can be generic + is to accept and return a list of values + where the length of that list can vary. How do you create a parameter pack? From 93185557dc8002a6f06b51233becf539c8d24a5a Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 1 May 2023 17:00:31 -0700 Subject: [PATCH 05/14] Use a working code example. --- TSPL.docc/LanguageGuide/Generics.md | 62 ++++++++++++++++++----------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index d46111717..ea95d31de 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -1855,47 +1855,56 @@ protocol ComparableContainer: Container where Item: Comparable { } ## Generic Parameter Packs -Parameter packs preserve type information -while also not depending on a specific number of arguments being passed in. -For example, -using parameter packs lets you avoid writing multiple overloads -that vary in the number of parameters they accept: +To understand the problem that parameter packs solve, +consider the following group of overloads: ```swift -func someFunction(_ x1: T) -> T { ... } -func someFunction(_ x1: T, _ x2: U) -> T, U { ... } -func someFunction(_ x1: T, _ x2: U, _ x3: V) -> T, U, V { ... } -``` +func double(_ value: T) -> T { + return 2 * value +} -The example above is repetitive and error prone, -and limits the function to a maximum of three arguments. -Using `Any`, as shown below, -allows an arbitrary number of arguments -at the expense of erasing type information. +func double(_ value1: T, _ value2: U) -> (T, U) { + return (2 * value1, 2 * value2) +} -```swift -func someFunction(_ x: [Any]) -> [Any] { ... } +func double( + _ value1: T, + _ value2: U, + _ value3: V) -> (T, U, V) { + return (2 * value1, 2 * value2, 2 * value3) +} ``` +Each of the overloads takes a different number of arguments, +and returns a tuple containing the result of doubling those arguments. +Manually writing out each function like this +is repetitive and error prone, +and limits the function to a maximum of three arguments. +Other similar approaches that also have drawbacks +include using an array, which requires all arguments to be the same type, +or using `Any` which erases type information. + Writing this function with a parameter pack preserves type information about its arguments, and lets you call the function an arbitrary number of arguments. ```swift -func someFunction(x: repeat each Element) - -> repeat each Element { ... } +extension Numeric { + func doubled() -> Self { + return 2 * self + } +} + +func double(_ value: repeat each T) -> (repeat each T) { + return ( repeat (each value).doubled() ) +} -func someFunction(x: repeat each T) -> repeat each T { ... } ``` In the code above, `Element` is a generic type parameter. It's marked `each Element`, indicating that it's a type-parameter pack. - - - - XXX OUTLINE: Why parameter packs? @@ -1969,6 +1978,13 @@ How do you access the values of a parameter pack? Other code can come between `repeat` and `each` --- both of those are different from `repeat (Int, each T)` +- Tripping hazard: + You can call methods on the repeated values, + as in `repeat (each x).doSomething` --- + and the grouping parentheses are required there --- + but you can't include operators like `repeat (1 + each x)` + as part of the pack expansion. + - You can expand a parameter pack's values only inside a tuple or a function call. (TR: Also inside arguments to a macro? From 3db53bb0718e422a471eaac53480afeb6809dd05 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 25 May 2023 22:36:58 -0700 Subject: [PATCH 06/14] Comment out outline, and keep writing. --- TSPL.docc/LanguageGuide/Generics.md | 50 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index ea95d31de..8b0e2fb29 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -1856,7 +1856,7 @@ protocol ComparableContainer: Container where Item: Comparable { } ## Generic Parameter Packs To understand the problem that parameter packs solve, -consider the following group of overloads: +consider the following overloads: ```swift func double(_ value: T) -> T { @@ -1875,43 +1875,55 @@ func double( } ``` -Each of the overloads takes a different number of arguments, +Each function in the example above +takes a different number of arguments, and returns a tuple containing the result of doubling those arguments. +Because the functions are generic, +they can take values of any numeric type, +and each argument can be a different type --- +and the doubled values that are returned have those same types. Manually writing out each function like this -is repetitive and error prone, -and limits the function to a maximum of three arguments. -Other similar approaches that also have drawbacks -include using an array, which requires all arguments to be the same type, +is repetitive and can be error prone. +It also imposes an arbitrary limit of three arguments, +even though the doubling operation could really apply +to any number of arguments. + +Other approaches that also have drawbacks +include taking the arguments an array or a variadic parameter, +which requires all arguments to be the same type, or using `Any` which erases type information. Writing this function with a parameter pack preserves type information about its arguments, -and lets you call the function an arbitrary number of arguments. +and lets you call the function an arbitrary number of arguments: ```swift +func double(_ value: repeat each T) -> (repeat each T) { + return (repeat (each value).doubled()) +} + extension Numeric { func doubled() -> Self { return 2 * self } } - -func double(_ value: repeat each T) -> (repeat each T) { - return ( repeat (each value).doubled() ) -} - ``` In the code above, `Element` is a generic type parameter. It's marked `each Element`, indicating that it's a type-parameter pack. +In contrast to generic type parameters, +which act like a blank where you fill in a single type, +generic type-parameter packs act like a list of blanks. +There isn't any syntax in Swift to write out the list of types, +but you use `repeat` and `each` +to mark code that is repeated for each value in that list. -XXX OUTLINE: - -Why parameter packs? +For example, +◊ call the function -- Another way that code can be generic - is to accept and return a list of values - where the length of that list can vary. + + ## Generic Subscripts Subscripts can be generic, From ba2681b4715714d58643e66cf09d20421a3d3c53 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 26 May 2023 16:03:07 -0700 Subject: [PATCH 07/14] Start expanding introduction of parameter packs. --- TSPL.docc/LanguageGuide/Generics.md | 35 +++++++++++++++--------- TSPL.docc/ReferenceManual/Expressions.md | 2 +- TSPL.docc/ReferenceManual/Types.md | 5 ++++ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index 8b0e2fb29..ad8cf5936 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -1895,32 +1895,41 @@ or using `Any` which erases type information. Writing this function with a parameter pack preserves type information about its arguments, -and lets you call the function an arbitrary number of arguments: +and lets you call the function with an arbitrary number of arguments: ```swift func double(_ value: repeat each T) -> (repeat each T) { - return (repeat (each value).doubled()) -} - -extension Numeric { - func doubled() -> Self { - return 2 * self - } + // ... } ``` In the code above, `Element` is a generic type parameter. It's marked `each Element`, indicating that it's a type-parameter pack. -In contrast to generic type parameters, -which act like a blank where you fill in a single type, -generic type-parameter packs act like a list of blanks. +In contrast to a generic type parameter, +which serves as a placeholder for a single type, +a type-parameter pack is a placeholder for multiple types. +The ability to have `T` contain a varying number of types +is what allows this version of `double(_:)` +to take any number of parameters +while preserving the type information about each of its parameters. + There isn't any syntax in Swift to write out the list of types, but you use `repeat` and `each` to mark code that is repeated for each value in that list. -For example, -◊ call the function +```swift +func double(_ value: repeat each T) -> (repeat each T) { + return (repeat (each value).doubled()) +} + + +extension Numeric { + func doubled() -> Self { + return 2 * self + } +} +``` + Writing this function with a parameter pack preserves type information about its arguments, and lets you call the function with an arbitrary number of arguments: @@ -1929,32 +1931,84 @@ extension Numeric { return 2 * self } } + +let numbers = [12, 0.5, 8 as Int8] +let doubledNumbers = double(numbers) ``` - +How do you work with errors? + +- Throwing or propagating an error stops iteration over the value pack. + +- For example, + to return a result only if none of the calls threw an error: + + ``` + do { + return (repeat try (each item).doSomething()) + } catch { + return nil + } + ``` ## Generic Subscripts From 9db2e32d7dafbc823bcf7b4949e8cab95c5d4dd6 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 10 Jul 2023 16:58:50 -0700 Subject: [PATCH 09/14] Update outline for new tuple behavior. Confirmed the code examples here and in SE-0399 work as of Swift 5.9 (swiftlang-5.9.0.120.7) --- TSPL.docc/LanguageGuide/Generics.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index e697c73cb..7feb1319f 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -2088,6 +2088,30 @@ How do you access the values of a parameter pack? there isn't a way to iterate over the values in a pack --- the SE proposal calls that out as a potential future direction. +- When a function returns a tuple using pack expansion, + or otherwise created by expanding a value pack, + you can perform the same operations on that tuple + as if it were still an unexpanded parameter pack. + This doesn't include tuples created any other way, + or tuples that contain a mix of elements created this way and another way. + + For example: + + + ```swift + func tuplify(_ value: repeat each T) -> (repeat each T) { + return (repeat each value) + } + + func example(_ value: repeat each T) { + let abstractTuple = tuplify(repeat each value) + repeat print(each abstractTuple) // Ok + + let concreteTuple = (true, "two", 3) + repeat print(each concreteTuple) // Invalid + } + ``` + How do you work with errors? - Throwing or propagating an error stops iteration over the value pack. From 7a10094cb11d35300ec2e99c2115bafd367bcbed Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 10 Aug 2023 18:00:53 -0700 Subject: [PATCH 10/14] Add a few more bits from SE-0393. --- TSPL.docc/GuidedTour/GuidedTour.md | 11 +++++++++++ TSPL.docc/LanguageGuide/Generics.md | 8 +++++++- TSPL.docc/ReferenceManual/Expressions.md | 20 ++++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/TSPL.docc/GuidedTour/GuidedTour.md b/TSPL.docc/GuidedTour/GuidedTour.md index 6af8adf38..2ca799077 100644 --- a/TSPL.docc/GuidedTour/GuidedTour.md +++ b/TSPL.docc/GuidedTour/GuidedTour.md @@ -2357,6 +2357,17 @@ anyCommonElements([1, 2, 3], [3]) Writing `` is the same as writing ` ... where T: Equatable`. +Use `repeat` and `each` to make generic functions +where the number of arguments can vary. + +``` +func printEach(_ t: repeat each T) { + repeat print(each t) +} + +printEach(1, "hello", true) +``` + > Beta Software: > > This documentation contains preliminary information about an API or technology in development. This information is subject to change, and software implemented according to this documentation should be tested with final operating system software. diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index 7feb1319f..13870f588 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -2007,7 +2007,7 @@ What else can you repeat? How do you repeat more than one type? - In the simple case, where only one type repeats, - this means you write `repeat each T` or similar. + you write `repeat each T` or similar. - For collections or other generic types, the pack expansion can happen inside, @@ -2035,6 +2035,12 @@ How do you constrain the types in a parameter pack? - In the more complex case, use `repeat each T ` in a trailing `where` clause. +- You must restrict the types that appear in a type-parameter pack; + conformance requirements don't implicitly propagate. + For example, given `each T: Hashable` writing `repeat Set` works, + but it doesn't work with just `each T` + because `Set` requires `T` to be hashable but the pack doesn't. + How do you access the values of a parameter pack? - Inside the function body, you use `repeat` diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index af80df12d..23ffce1a5 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -1527,7 +1527,7 @@ A single expression inside parentheses is a parenthesized expression. XXX OUTLINE: -- a `repeat` expression must contain one or more `each` expressions +- A `repeat` expression must contain one or more `each` expressions and has the shape `repeat <#repetition pattern#>` - TODO list of contexts where expansion is supported @@ -1538,11 +1538,27 @@ XXX OUTLINE: - The *repetition pattern* is repeated once for each type in the pack -- all of the `each` expressions must expand packs with the same number of types +- If an expression includes both the `repeat` operator and + a `try` or `await` operator, + the `repeat` operator must appear first. + (So `repeat try each foo` or `repeat each try foo`) + +- All of the `each` expressions in a parameter-pack expression + must expand packs that have the same number of types. + +- In a function declaration, + an argument whose type is a parameter-pack expansion type + must be the last parameter + or the parameter after it must have a label. - It's valid for a pack expression contain no elements, in which case the parameter-pack expansion expression isn't evaluated at all (zero times) + + > Grammar of a pack-expansion expression: > > *parameter-pack-expression* → **`each`** *expression* From 90aa68ddba0a1bd522ec640a3ecda026e10a5e06 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 18 Aug 2023 10:16:19 -0700 Subject: [PATCH 11/14] Use the right word. Co-authored-by: Holly Borla --- TSPL.docc/LanguageGuide/Generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index 13870f588..1003d7243 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -1953,7 +1953,7 @@ How do you read a parameter pack at its call site? A pack that's made up of types is called a *type pack*. A pack that's made up of values is called a *value pack*. -- A value pack provides the types for a value pack. +- A type pack provides the types for a value pack. The corresponding types and values appear at the same positions in their respective packs. From 07b58edbdcfc5e928aa3e2cefe931b70b34675d4 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Sat, 11 Nov 2023 22:41:35 -0800 Subject: [PATCH 12/14] Add a paragraph about pack iteration Cherry-picked from https://github.com/apple/swift-book/pull/206 --- TSPL.docc/ReferenceManual/Statements.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/TSPL.docc/ReferenceManual/Statements.md b/TSPL.docc/ReferenceManual/Statements.md index 8f9913cf7..b2b1ca591 100644 --- a/TSPL.docc/ReferenceManual/Statements.md +++ b/TSPL.docc/ReferenceManual/Statements.md @@ -70,19 +70,19 @@ and a `continue` statement and is discussed in A `for`-`in` statement allows a block of code to be executed once for each item in a collection (or any type) that conforms to the -[`Sequence`](https://developer.apple.com/documentation/swift/sequence) protocol. +[`Sequence`](https://developer.apple.com/documentation/swift/sequence) protocol, or in a value parameter pack. A `for`-`in` statement has the following form: ```swift -for <#item#> in <#collection#> { +for <#item#> in <#expression#> { <#statements#> } ``` -The `makeIterator()` method is called on the *collection* expression -to obtain a value of an iterator type --- that is, -a type that conforms to the +If the *expression* of a `for`-`in` statement is a collection *expression*, the +`makeIterator()` method is called on it to obtain a value of an iterator type + --- that is, a type that conforms to the [`IteratorProtocol`](https://developer.apple.com/documentation/swift/iteratorprotocol) protocol. The program begins executing a loop by calling the `next()` method on the iterator. @@ -93,6 +93,16 @@ and then continues execution at the beginning of the loop. Otherwise, the program doesn't perform assignment or execute the *statements*, and it's finished executing the `for`-`in` statement. +The *expression* of a `for`-`in` statement may also be a pack expansion +*expression*. In this case, on the *i*th iteration, the type of *item* is the +*i*th type parameter in the type parameter pack being iterated over. The value +of *item* is the pattern type of *expression*, with each captured type +parameter pack replaced with an implicit scalar type parameter with matching +requirements. The *expression* is evaluated once at each iteration, instead of +`n` times eagerly, where `n` is the length of the packs captured by the +pattern. The program will continue executing *statements* while the total +length of the packs captured by the pattern isn't reached. + > Grammar of a for-in statement: > > *for-in-statement* → **`for`** **`case`**_?_ *pattern* **`in`** *expression* *where-clause*_?_ *code-block* From 41bc1342284d208c82c33b6bd713a9f1d1b9b30a Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 2 Jul 2024 16:58:27 -0700 Subject: [PATCH 13/14] Use semantic line breaks Removed stray whitespace at end of lines. Restored line breaks for unchanged content. Split lines in new content at clause and sentence boundaries, instead of wrapping at 80 columns. --- TSPL.docc/ReferenceManual/Statements.md | 33 +++++++++++++++---------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/TSPL.docc/ReferenceManual/Statements.md b/TSPL.docc/ReferenceManual/Statements.md index b2b1ca591..4f7e9597d 100644 --- a/TSPL.docc/ReferenceManual/Statements.md +++ b/TSPL.docc/ReferenceManual/Statements.md @@ -70,7 +70,8 @@ and a `continue` statement and is discussed in A `for`-`in` statement allows a block of code to be executed once for each item in a collection (or any type) that conforms to the -[`Sequence`](https://developer.apple.com/documentation/swift/sequence) protocol, or in a value parameter pack. +[`Sequence`](https://developer.apple.com/documentation/swift/sequence) protocol, +or in a value parameter pack. A `for`-`in` statement has the following form: @@ -80,9 +81,10 @@ for <#item#> in <#expression#> { } ``` -If the *expression* of a `for`-`in` statement is a collection *expression*, the -`makeIterator()` method is called on it to obtain a value of an iterator type - --- that is, a type that conforms to the +If the *expression* of a `for`-`in` statement is a collection *expression*, +the `makeIterator()` method is called on it +to obtain a value of an iterator type --- that is, +a type that conforms to the [`IteratorProtocol`](https://developer.apple.com/documentation/swift/iteratorprotocol) protocol. The program begins executing a loop by calling the `next()` method on the iterator. @@ -93,15 +95,20 @@ and then continues execution at the beginning of the loop. Otherwise, the program doesn't perform assignment or execute the *statements*, and it's finished executing the `for`-`in` statement. -The *expression* of a `for`-`in` statement may also be a pack expansion -*expression*. In this case, on the *i*th iteration, the type of *item* is the -*i*th type parameter in the type parameter pack being iterated over. The value -of *item* is the pattern type of *expression*, with each captured type -parameter pack replaced with an implicit scalar type parameter with matching -requirements. The *expression* is evaluated once at each iteration, instead of -`n` times eagerly, where `n` is the length of the packs captured by the -pattern. The program will continue executing *statements* while the total -length of the packs captured by the pattern isn't reached. +The *expression* of a `for`-`in` statement +may also be a pack expansion *expression*. +In this case, +on the *i*th iteration, +the type of *item* is the *i*th type parameter +in the type parameter pack being iterated over. +The value of *item* is the pattern type of *expression*, +with each captured type parameter pack replaced with +an implicit scalar type parameter with matching requirements. +The *expression* is evaluated once at each iteration, +instead of `n` times eagerly, +where `n` is the length of the packs captured by the pattern. +The program will continue executing *statements* +while the total length of the packs captured by the pattern isn't reached. > Grammar of a for-in statement: > From 6c2cd8f4af4ad60682d5c24750bc30b88720c60b Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Wed, 7 Aug 2024 18:20:20 -0700 Subject: [PATCH 14/14] Review feedback --- TSPL.docc/LanguageGuide/Generics.md | 21 ++++++++++----------- TSPL.docc/ReferenceManual/Expressions.md | 5 +++-- TSPL.docc/ReferenceManual/Types.md | 16 +++++++++++----- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index 1003d7243..14bffb21b 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -1889,7 +1889,7 @@ even though the doubling operation could really apply to any number of arguments. Other approaches that also have drawbacks -include taking the arguments an array or a variadic parameter, +include taking the arguments as an array or a variadic parameter, which requires all arguments to be the same type, or using `Any` which erases type information. @@ -1905,12 +1905,11 @@ func double(_ value: repeat each T) -> (repeat each T) { } ``` -In the code above, `Element` is a generic type parameter. -It's marked `each Element`, -indicating that it's a type-parameter pack. +In the code above, `each T` is declared in a generic parameter list. +It's marked with `each`, indicating that it's a type parameter pack. In contrast to a generic type parameter, which serves as a placeholder for a single type, -a type-parameter pack is a placeholder for multiple types. +a type parameter pack is a placeholder for multiple types. The ability to have `T` contain a varying number of types is what allows this version of `double(_:)` to take any number of parameters @@ -1933,7 +1932,7 @@ extension Numeric { } let numbers = [12, 0.5, 8 as Int8] -let doubledNumbers = double(numbers) +let doubledNumbers = doubled(numbers) ``` The value of `doubledNumbers` is `(24, 1.0, 16)`, @@ -1980,7 +1979,7 @@ How do you create a parameter pack? and in function argument lists. ```swift - func f(_ t: repeat each T) -> repeat each T + func f(_ t: repeat each T) -> (repeat each T) ``` - The expansion pattern is repeated for every element in the given type pack @@ -2035,8 +2034,8 @@ How do you constrain the types in a parameter pack? - In the more complex case, use `repeat each T ` in a trailing `where` clause. -- You must restrict the types that appear in a type-parameter pack; - conformance requirements don't implicitly propagate. +- You must restrict the types that appear in a type parameter pack + if its expansion will be used as a restricted generic type parameter. For example, given `each T: Hashable` writing `repeat Set` works, but it doesn't work with just `each T` because `Set` requires `T` to be hashable but the pack doesn't. @@ -2062,7 +2061,7 @@ How do you access the values of a parameter pack? ``` - The result (and its type) of expanding a parameter pack - vary depending on the number of elements in the pack. + within a tuple vary depending on the number of elements in the pack. Zero-element packs produce `()`, single-element packs produce a simple type, and multi-element packs produce a tuple type. @@ -2073,7 +2072,7 @@ How do you access the values of a parameter pack? For example, `repeat (each T, Int)` is different from `(repeat each T, Int)`. - The former makes multiple tuple `(T1, Int) ... (Tn, Int)`, + The former makes multiple tuples `(T1, Int) ... (Tn, Int)`, expanding `T` and adding `Int` to each tuple. The latter makes one tuple, `(T1, ..., Tn, Int)`. Other code can come between `repeat` and `each` --- diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index dd27bfbf9..3502f4a36 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -1489,6 +1489,7 @@ XXX OUTLINE: + in a tuple, producing tuple elements + as a statement, including at top level, repeating the statement's expression + but not in a function call, producing arguments + + in a `for`-`each` loop - The *repetition pattern* is repeated once for each type in the pack @@ -1497,7 +1498,7 @@ XXX OUTLINE: the `repeat` operator must appear first. (So `repeat try each foo` or `repeat each try foo`) -- All of the `each` expressions in a parameter-pack expression +- All of the `each` expressions in a pattern expression must expand packs that have the same number of types. - In a function declaration, @@ -1505,7 +1506,7 @@ XXX OUTLINE: must be the last parameter or the parameter after it must have a label. -- It's valid for a pack expression contain no elements, +- It's valid for a pack expression to contain no elements, in which case the parameter-pack expansion expression isn't evaluated at all (zero times)