From 377d6bc537431af88a126cbaef30b4f760510d24 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 4 Apr 2023 17:04:35 -0700 Subject: [PATCH 01/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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) +Parameter packs make it possible to abstract such function over the +number of generic arguments, eliminating the need to type-erase or +provide multiple overloaded copies of the same function with +a variable number of generic arguments. Writing this function with a parameter pack preserves type information about its arguments, @@ -1921,23 +1924,17 @@ to mark code that is repeated for each value in that list. ```swift func double(_ value: repeat each T) -> (repeat each T) { - return (repeat (each value).doubled()) + return (repeat (each value) * 2) } - -extension Numeric { - func doubled() -> Self { - return 2 * self - } -} - -let numbers = [12, 0.5, 8 as Int8] -let doubledNumbers = doubled(numbers) +let doubledNumbers = double(12, 0.5, 8) +print(doubledNumbers) +// Prints (24, 1.0, 16) ``` The value of `doubledNumbers` is `(24, 1.0, 16)`, and each element in this tuple has the same type -as the value in `numbers` that it comes from. +as the value in the value pack that it comes from. Both `12` and `24` are `Int`, `0.5` and `1.0` are `Double`, and `8` and `16` are `Int8`. From ac3bab0b4f9d8ff80262ffb5596a194af31b038c Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Wed, 30 Jul 2025 17:08:59 -0700 Subject: [PATCH 16/17] Touch on iteration, naming convention, nit on requiring one element. --- TSPL.docc/LanguageGuide/Generics.md | 101 +++++++++++++++------------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index 0784af6dd..7dd940324 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -1927,7 +1927,7 @@ func double(_ value: repeat each T) -> (repeat each T) { return (repeat (each value) * 2) } -let doubledNumbers = double(12, 0.5, 8) +let doubledNumbers = double(12, 0.5, 8 as Int8) print(doubledNumbers) // Prints (24, 1.0, 16) ``` @@ -1939,65 +1939,76 @@ Both `12` and `24` are `Int`, `0.5` and `1.0` are `Double`, and `8` and `16` are `Int8`. -* * * +There are two types of parameter packs: type packs and value packs. The type +packs are made of type parameters, while value packs are made of values. -XXX OUTLINE: +At the call site of `double`, the type of the pack `T` will be substituted +for the type pack `{Int, Double, Int8}`. The type of the type pack is derived +from the value pack `12, 0.5, 8 as Int8` passed at the call site. This +guarantees that the corresponding types and values appear at the same +positions in their respective packs. -How do you read a parameter pack at its call site? +The `double` function doubles each element of the parameter pack by applying +the `repeat` statement to each element of the value pack multiplied by 2. +The `repeat` statement applied to a value pack denotes a "repetition pattern", +which is a pattern that will be repeated for every value in the pack. -- A parameter pack "packs" together types. - 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*. +Repetition is a great way to apply a simple transformation on each element +of the parameter pack. It effectively performs iteration on the value pack, +but in a clean and concise way. However, there are cases where your code either +demands a piece of other state to be updated on each iteration, or you want the +iteration to stop when a certain condition is met. -- A type pack provides the types for a value pack. - The corresponding types and values appear at the same positions - in their respective packs. +Similarly to collections, value packs support iteration with a `for`-`in repeat` +loop. -- When you write code that works on collections, you use iteration. - Working with parameter packs is similar --- - except each element has a different type, - and instead of iteration you use repetition. +```swift +func allEmpty(_ array: repeat [each T]) -> Bool { + for a in repeat each array { + guard a.isEmpty else { return false } + } -How do you create a parameter pack? + return true +} +``` -- In the generic type parameters, - write `each` in front of a generic argument - to indicate that this argument creates a type parameter pack. +In the code above, the `allEmpty` function is generic over a type parameter +pack `each T`, and takes a value parameter pack `array`, the type of which is +declared using `repeat [each T]` pack expansion, where `[each T]` is the +repetition pattern. -- In the function's parameters, - write `repeat` in front of the type for the parameter - that can accept a variable number of arguments, - to create an *expansion pattern*. - You can also write `repeat` in the function's return type. +The `for`-`in repeat` loop gives an opportunity to access elements of the value +pack on each iteration using a local variable. In this case, the local variable +`a` represents the value stored in the value pack on a given iteration. Using +`for`-`in repeat` loop gives you an opportunity to stop the iteration early—in +this case, the iteration will stop once a non-empty array is found. -- The expansion of a repetition pattern produces a comma-separated list. - It can appear in generic argument lists, - in tuples types, - and in function argument lists. +Here's how you might use the `allEmpty` function: - ```swift - func f(_ t: repeat each T) -> (repeat each T) - ``` +```swift +print(allEmpty(["One", "Two"], [1], [true, false], [])) +// False +``` -- The expansion pattern is repeated for every element in the given type pack - by iterating over the types in the type pack - and replacing the type placeholder that comes after `each`. +Using the `for`-`in repeat` loop is useful in cases where you want more control +over the pack iteration—stop the evaluation early for performance, or mutate +some other state on each iteration. - For example, expanding `repeat Request` - where the `Payload` type pack contains `Bool`, `Int`, and `String` - produces `Request, Request, Request`. +Note that both approaches for iterating over the pack are fine! Choose one that +fits your use-case the most. -- When the type of a function argument is a type pack, - the values that are passed in for that argument become a value pack. +> Note: Use singular naming convention when naming your parameter packs. -- Naming convention: - Use singular names for parameter packs, - and plural names only in argument labels. +Parameter packs can contain zero or more arguments. If you need to +require one or more, use a regular generic argument together with the type +pack argument: + +```swift +func requireOneOrMore(element: (T, repeat each U)) { } +``` -Note: -Parameter packs can contain zero or more arguments. -If you need to require one or more, -use a regular parameter before the pack parameters. +The function above can now be called with at least one value passed into the +`element` parameter, required by the `T` generic argument. What else can you repeat? How do you repeat more than one type? From a8ca96d6f5e44cbcaa4cd656a9b8bc2e907fae70 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Thu, 31 Jul 2025 17:05:23 -0700 Subject: [PATCH 17/17] Add more advanced examples, clean up the TODOs. --- TSPL.docc/LanguageGuide/Generics.md | 252 ++++++++++++++-------------- 1 file changed, 130 insertions(+), 122 deletions(-) diff --git a/TSPL.docc/LanguageGuide/Generics.md b/TSPL.docc/LanguageGuide/Generics.md index 7dd940324..3e6b8c330 100644 --- a/TSPL.docc/LanguageGuide/Generics.md +++ b/TSPL.docc/LanguageGuide/Generics.md @@ -2010,135 +2010,143 @@ func requireOneOrMore(element: (T, repeat each U)) { } The function above can now be called with at least one value passed into the `element` parameter, required by the `T` generic argument. -What else can you repeat? -How do you repeat more than one type? - -- In the simple case, where only one type repeats, - you write `repeat each T` or similar. - -- 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 vary the return types, based on the parameter types? -(TODO: Worked code example. -Use WWDC 2023 session 10168 example at 16 minutes -as a starting point.) - -How do you constrain the types in a parameter pack? - -- In the simple case, - write the `where` clause in the generic parameters list. - `func foo` - requires all of the types passed in the `T` type pack to conform. - -- 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 - 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. - -How do you access the values of a parameter pack? - -- Inside the function body, you use `repeat` - to mark places where the code expands the elements of a parameter pack. - -- 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)) - ``` +Repetition patterns can be applied to so many types! In the the `nonEmpty` +example above, the `array` parameter's type is declared with `repeat [each T]`, +expanding into multiple array types. -- The result (and its type) of expanding a parameter 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. - -- 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 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` --- - 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? - 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. - -- 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) - } +A more complex type can include `each` multiple times. For example: - func example(_ value: repeat each T) { - let abstractTuple = tuplify(repeat each value) - repeat print(each abstractTuple) // Ok +```swift +func evaluateAll( + result: repeat Result +) -> [any Codable] { + // ... +} +``` - let concreteTuple = (true, "two", 3) - repeat print(each concreteTuple) // Invalid - } - ``` +The above `evaluateAll` function is generic over two different type packs, +`V` and `E`. The type of `result` is declared using the `Result` type that +takes 2 generic parameters. `repeat Result` expands the +type of the `result` parameter to be a type pack where the expansion of +`each V` and `each E` is required to be of the same size, meaning the +number of types both packs expand to are guaranteed to be equal. -How do you work with errors? +You can also vary the return types of your function based on the parameter +types: -- Throwing or propagating an error stops iteration over the value pack. +```swift +protocol RequestProtocol { + associatedtype Input + associatedtype Output + func evaluate(_ input: Input) -> Output +} -- For example, - to return a result only if none of the calls threw an error: +struct Evaluator { + var item: (repeat each Request) - ``` - do { - return (repeat try (each item).doSomething()) - } catch { - return nil - } - ``` + func query(_ input: repeat (each Request).Input) -> (repeat (each Request).Output) { + return (repeat (each item).evaluate(each input)) + } +} +``` + +In the above code example, the protocol `RequestProtocol` requires a function +`evaluate` that, given an input of an associated type `Input`, returns the +other associated type `Output`. + +The `Evaluator` struct is generic over the `Request` type, which is a parameter +pack. Like with regular generics, parameter packs support type constraints—each +element in the `Request` type pack has to conform to the `RequestProtocol` +protocol. + +The `item` property stores the elements of the `Request` parameter +pack in a tuple. + +The `query` methods operates on an `input` argument of +`repeat (each Request).Input` type. Because each element of the `Request` +type pack conforms to `RequestProtocol`, the implementation of `query` is able +to call the protocol requirement `evaluate`, using the elements of the `input` +parameter pack as its input. As a result, a new pack containing +`RequestProtocol.Output` type is produced. + +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`. In this example, the `repeat` +keyword applied in front of `(each item).evaluate(each input)` call +calls `evaluate` function on each pair of elements from `item` and `input` +value packs, respectively. Again, such construction guarantees that `item` +and `input` packs have to be of the same length. As a result, the tuple +returned by `query` also has the same length. + +You can also use trailing `where` clause with parameter packs in more complex +cases. For example, consider the following type: + +```swift +struct Container { + var value: (repeat each Element) +} +``` + +With the `where` clause, it is easy to conditionally constrain the type to +conform to protocols using the types in the `Element` parameter pack: + +```swift +extension Container: Sendable where repeat each Element: Sendable {} +``` + +In the code above, `Container` conditionally conforms to `Sendable` when each +element of the type parameter pack `Element` conforms to `Sendable`, making it +usable in contexts requiring sendability. + +The types that appear in a type parameter pack must be constrained if its +expansion is used as a constrained generic type parameter. For example, +types such as `Set` require its generic argument to conform to `Hashable`, +so each element of the parameter pack needs to be constrained: + +```swift +struct Container { + var value: (repeat each Set) +} +``` + +Using all of the concepts above, parameter packs provide powerful capabilities +in your code. Let's consider a more advanced example of using parameter packs, +and start off by declaring the following protocol: + +```swift +protocol ValueProducer { + associatedtype Value: Codable + func evaluate() -> Value +} +``` + +The above protocol `ValueProducer` requires the `evaluate()` method that’s +return type is the associated type `Value` that conforms to `Codable` protocol. + +For example, suppose you get a parameter pack of values of type +`Result`, and you need to iterate only over the success +elements and call the `evaluate()` method on its value. Also, suppose you need +to save the result of each call into an array. + +```swift +func evaluateAll(result: repeat Result) -> [any Codable] { + var evaluated: [any Codable] = [] + for case .success(let valueProducer) in repeat each result { + evaluated.append(valueProducer.evaluate()) + } + + return evaluated +} +``` + +In the body of `evaluateAll`, you can use the `for`-`in repeat` loop +for performing an operation while +iterating over the values stored in the `result` value pack. Note that you +can use a familiar `for case` pattern to only iterate over the `.success` +case of the `Result` enum. You can bind the value using the `valueProducer` +variable. Finally, on each iteration, you can append the result of the call +to the `evaluate()` method to the `evaluated` array, which gets returned +from the function. ## Generic Subscripts