Variadic Generics

I don't know if it has already been told, but in the Variadic Values subsection it is stated:

A variadic value can never leave a generic context ⏤ it must always be transformed in something else to do so.

In the provided example, however, you define properties having as type a variadic type:

extension SimpleVariadic {
  var someTs: T { /* ... */ }

  func testCollection() -> (T...) {
    // All your `map`, `filter`, etc are here!
    let arr1: [String] = someTs.map { "\($0)" }
    let arr2: [P1] = someTs.filter { $0 is Int }
    let first: P1? = someTs.first
    
    return (someTs...)
  }
}

let v: SimpleVariadic<Int, String> = /* ... */

// `v.som` shows `Collection | someTs`
v.som

for elem in v.someTs {
  print(type(of: elem))
}

Wouldn't .someTs be unaccessible due to being a variadic value? (T...) tuple type would be usable, but T variadic type would not, am I correct? If so, and if .someTs effectively is a tuple, we would need .map, .filter, .reduce, .allSatisfy, .first, etc. to be available on tuples themselves.

I'm re-evaluating @Joe_Groff's suggestion to keep tuples as structural types and as building blocks for generic variadics:

On the other hand, we have @Chris_Lattner3 goal of explicitly addressing non-nominal types conformances:

Did you introduce structural variadic types <A, B, ...> in order to let tuples become nominal types?
If so, a variadic generic primitive interface may not be enough: tuples do require parameter type labels and unfortunately not in the way addressed in this pitch.
You suggest explicit parameter type labels as described in the Generics Manifesto: Named generic parameters subsection, but tuple type labels are different in that regard:

// different type labels lead to different types
(a: Int, b: Double).self != (Int, Double).self
Tuple<a: Int, b: Double>.self != Tuple<Int, Double>.self

// with your syntax
struct Tuple<A, B> { ... }
Tuple<A: Int, B: Double>.self == Tuple<Int, Double>.self

// with explicit parameter type labels (strawman syntax ahead)
struct Tuple<a A, b B> { ... }
Tuple<a: A = Int, b: B = Double>.self == (a: Int, b: Double).self
struct Tuple<_ A, _ B> { ... }  // equivalent to struct Tuple<A, B>
Tuple<_: A = Int, _: B = Double>.self == (Int, Double).self

Tuple<a: A = Int, b: B = Double>.self != Tuple<A = Int, B = Double>.self

// note: "A = Int" and "B = Double" are there just for clarity
// you would use the type as Tuple<a: Int, b: Double> directly
// mirroring how functions work (almost)

// note: redeclaration of Tuple is there just for clarity

This is a minor and trivial problem to solve. The real problem surfaces when you want to actually declare a Tuple structure in the standard library. Tuples not only require an indefinite amount of types, but also variable type parameter labels. In the past has been suggested something like the following (sorry, I failed to find the actual post... searching in these forums is quite hard):

struct Tuple<...labels: T...> { ... }

For each label and type you would need to create an index property (i.e. .0, .1, .2, etc. up to the number of types involved in the tuple, which are not valid identifier names for struct properties) and a property named as the label, all statically available.

These suggest that:

  • your variadic generic structural interface type would need to handle/store optional labels, just like tuples already do;
  • tuples would still need some hardcoded properties (.0, .1, .2, etc.) not declarable in a standard library struct.

So, if tuples are special, why not use them as the de-facto interface for variadic generics?

1 Like