I'm trying to wrap my mind around why this is only true in the absence of more parameters.
func identity<Element0, each Element>(
_ element0: Element0, _ element: repeat each Element
) -> (Element0, repeat each Element) {
(element0, repeat each element)
}
identity(true, ()) as (Bool, Void) // compiles
identity(true) as (Bool, Void) // Cannot convert value of type 'Bool' to type '(Bool, Void)' in coercion
I thought maybe attempting these casts would help me understand, but they crash the compiler.
func identity<each Element>(_ element: repeat each Element) -> (repeat each Element) {
(repeat each element)
}
_ = identity as () -> Void
_ = identity as (Void) -> Void
This is also made confusing by the fact that between the two cases, the visible parentheses in the expression (repeat each element) end up corresponding to different things in the resultant value. When each Element == {}, the parentheses constitute the empty tuple itself, whereas when each Element == {()} the parentheses are the outer bounds of an identity expression wrapping the empty tuple (which, of course, ends up evaluating to the empty tuple all the same).
Swift doesn't have a concept of single-element tuples that are distinct from their underlying type. So the thing that's 'special' here isn't the empty tuple type (), it's the single-element/identity type (T) which is always equivalent to T (even when T == ()). And this equivalence ceases to apply once you add additional elements.
We treat an unlabeled tuple of one element as being just a parenthesization of the one element type, so Foo, (Foo), ((Foo)), etc. are all the same type. When you expand a pack into a tuple type, and the result has one element in total, then you end up with the single parenthesized element type. So in your first case:
func identity<each Element>(_ element: repeat each Element) -> (repeat each Element)
when you call identity with zero arguments, then zero elements get expanded into the return type, and you end up with the empty tuple (). When you call identity with one () argument, that one () gets expanded into the tuple as (()), which is the same type as (). In your second example:
func identity<Element0, each Element>(
_ element0: Element0, _ element: repeat each Element
) -> (Element0, repeat each Element) {
(element0, repeat each element)
}
calling identity with element0 but no element arguments means that you expand no additional tuple elements into (Element0, repeat each Element), so you end up with (Element0), which is equivalent to Element0. So identity(true) as (Bool, Void) is a type mismatch, but identity(true) as Bool would work, since the result of the expansion is (Bool).
Not quite. The return type here is (repeat each Element). We can apply two substitutions:
Element := Pack{} gives you ().
Element := Pack{()} also gives you () because one-element tuples are unwrapped. For the same reason Element := Pack{Int} gives you Int.
Element := Pack{(), ()} gives you ((), ()), a tuple type of arity 2.
Element := Pack{Int, Int} gives you (Int, Int), another tuple type of arity 2.
These are all tuple types of different arity.
This requirement will totally be supported, but it means that each element of the Element pack is (), but the length of the pack is arbitrary. So if you have
func foo<each Element>(_: repeat each Element) where repeat each Element == ()
Then these are all valid ways to call foo():
foo()
foo(())
foo((), ())
foo((), (), ())
...
But you can't call foo() with any other argument type.
It's just notional syntax for the substitution. So "Element := Pack{} gives you ()" means that when Element is an empty pack, (repeat each Element) is the empty tuple.
Yes, thatās what Iām getting at in the quoted portionāthe identity function should be viewed as taking a parameter list and returning the result of wrapping that parameter list in parentheses. In the case of an empty parameter list, you get an empty list wrapped in parentheses, that is, (), the empty tuple. When you have two or more elements in the parameter list, you get back a tuple of those elements, e.g., (āoneā, 2).
However, when you have only a single element t in the parameter list, even if that single element is itself a tuple, what you get back is t wrapped in parentheses, and Swift treats t wrapped in parentheses as equivalent to t, so you donāt end up with the āadditional level of tuplingā as in the other cases.