Awesome to see this moving forward.
I've always found the ...
spelling for variadic generics somewhat problematic given the noted extensive overloading of this piece of syntax. I don't know if I agree with @xwu's intuition that (T...)...
is particularly strange. With an appropriate operator for an explicit tuple splat, it seems not-outlandish to me to have a function such as:
func apply<T..., U>(fn: (T...) -> U, _ args: (T...)...) -> [U] {
return args.map { fn(#splat($0)) }
}
The bigger issue for me when it comes to the ...
spelling is how it pans out for generic types. From the body of the example ZipSequence
type:
public init(_ seqs: Sequences...) {
self.seqs = seqs
}
The only way we can tell for sure that Sequences
is a variadic type sequence rather than seqs
being a variadic parameter is to inspect the generic signature where Sequences
is declared. While this is not so much of an issue for functions (since the generic parameter will always be fairly local to its use), type members may be defined in a separate extension, which means they could be in a disparate part of the file, another file, or another module entirely.
I'd really like a spelling here that indicates at the point of use of a particular type sequence that we're dealing with a type sequence. It shouldn't be something that's hard for the reader to determine. I think previous pitches have used prefix ...
for this reason, which I don't love because it's not super distinctive from postfix, but at least it has the advantage that variadic parameters and generic type sequence parameters can be differentiated locally.
Somewhat relatedly, I think it would be good to think about how the syntax will relate to conventions around the naming of these type sequences. In some places the name associated with the type sequence is plural (Sequences
, Views
, Xs
, Ys
) but in others, it's a singular noun describing the entirety of the sequence (Tail
, Init
), and in still others we're using just single letter generic names (T
, U
).
The pitch says that when T...
is a type sequence parameter,
T
refers to a particular opened archetype of an element of the type sequence T...
which suggests to me that there are situations where the plural spelling might make a function signature more difficult to understand, e.g.:
func allPresent<Sets..., Elements...>(haystacks: Sets..., needles: Element...) -> Bool
where Sets == Set<Elements>
I love that Swift has really leaned in to meaningful, more-than-one-letter names for generic parameters, so the extent to which the variadic generics design is able to encourage meaningful names as opposed to Ts...
and Us...
is something I think is important.
I could imagine us introducing, for instance, a new type of generic constraint that would allow us to supply a name for each element in a type sequence. Something like:
func allPresent<Haystacks..., Needles...>(haystacks: Haystacks..., needles: Elements...) -> Bool
where forEach(Haystack in Haystacks, Needle in Needles, Haystack == Set<Needle>)
Minor note, but for this portion:
This also allows for stored properties of type sequence type within these nominal types.
[...]
As with functions, stored properties in specializations of these types canonicalize to tuples
IMO a stored property of type T...
should be illegal, and the user should have to write (T...)
to get the tuple behavior. Bare T...
in a function parameter list doesn't mean "tuple" and I don't think we should confuse the two.