A vision for variadic generics in Swift

I imagine that runtime pattern matching / pack destructuring would be done using type coercions with as, because that's the syntax we already have, and because there's probably more than the pack-ness that you want to match against. For example:

switch (element...) {
  case let (first, rest) as (First, Rest...):
    // do something with 'first' and 'rest'
  default:
    break
}

This also may be a use case for generalized opaque types for local variables to introduce new generic parameters that can be used for pattern matching.

In my opinion, whether or not value packs are declared with ... after the variable name is orthogonal to the pattern matching syntax; if we decide to use let value... for value packs, we should do it everywhere, including parameter declarations:

func tuplify<T...>(_ t...: T...) { (t...) }

Personally, I find the ... after t to be redundant, and it makes it a little more difficult to think about t: T as the element that is repeated by the ..., but of course that's just the way I think about the code.

Unfortunately it's not just about the postfix range operator .... The partial range operator intentionally uses the same operator as the regular closed range infix operator 1 ... x.count. Then there's also the closely related half-open range operator 1 ..< x.count that intentionally matches the structure of the closed-range operator, just with a different final character to denote the open/closed property of the range. While it might be feasible to change all of these operators to be spelled differently, these are such widely used operators that I don't think it would be worth the tradeoff.

If we're concerned about the existing uses of ... in Swift, I think we should instead consider using a different syntax for variadic generics. I'll admit the * alternative is growing on me. In any case, I'm going to start a dedicated thread to bikeshed the ... syntax for variadic generics so we can figure it out!

No, the operation to access tuple elements as a pack is fundamentally necessary in this design, because otherwise, there would be no way to expand a tuple element-wise in the pattern of a pack expansion. We could choose to instead provide a way to expand a tuple into a local variable pack, and in fact the original draft of this vision document included a "value expansion operation" instead of a direct operation to access tuple elements as a pack, but generalizing tuple types became super annoying because let element = tuple... was the first line of every function that accepted an abstract tuple.

The section of the document comparing tuples and packs explains this in more detail, and provides a few examples of the difference between using a tuple in the pattern of a pack expansion vs using the tuple elements as a pack.

Neat! I didn't think about this, but I agree that guard let and friends should be supported with local variable packs.

1 Like