Variadic Generics

generics

(Andrea Tomarelli) #1

Hello everyone, I was reading the Generics Manifesto document and saw the Variadic Generics section. I would like to start a pitch about them!
This post mainly contains elements directly taken from the Generics Manifesto plus some personal thoughts and comments.

Disclaimer: I'm not a native English speaker so my writing could be less-thant-clear in some instances; sorry for that, I'll eventually try to explain anything that is not clear.

Basics: variadic generics are tuples

The document refers to C++ parameter pack and ... (ellipsis) syntax, that I personally do not particularly dislike, and then at the end of the section refers to a tuple splat operator.

I think that variadic generics can be represented as tuples once "instantiated", and there is a similar precedent in the language too with variadics function argument becoming Arrays. I say "I think" because I'm not actually sure that this is what the manifesto suggests - given the iterators example in which it talks about "zero or more stored properties" instead of a tuple of iterators instances.

struct Variadic<...T> {
  var (...items): (T...)
}

let v: Variadic<Int, Float, Any> = ...
// `v` is
// Variadic<(Int, Float, Any)> {
//   var items: (Int, Float, Any)
// }

This means that a type with variadic generics is actually a type with a single generic parameter of tuple type, whose number of elements depend on the actual usage. This even accounts for the case in which no generic are specified: the variadic parameter is represented by the empty tuple.
In this way one can directly reference any member via tuple access, and can use common members (if any) on the compound value itself.
In the previous example one cannot use any member on items, because T is unconstrained and in Swift there are no members common to all types. But if the declaration was T: Collection you could use first on items and obtain a tuple containing the first item in each sequence (if any):

struct Variadic<...T: Collection> {
  var (...collections): (T...)
}

let v: Variadic<[Int], [Float], [Any]> = ...
// `v` is
// Variadic<([Int], [Float], [Any])> {
//   var collections: ([Int], [Float], [Any])
// }

let result = (v.collections.first...)
// result has type (Int, Float, Any)

Pattern matching

The Generics Manifesto uses the following example:

var (...iterators): (Iterators...)

[...]

guard let values = (iterators.next()...) else {   // call "next" on each of the iterators, put the results into a tuple named "values"
  reachedEnd = true
  return nil
}

If the syntax (variadicPack.someMember...) means get someMember from all the items of variadicPack and put them, in order, in a new tuple, then (iterator.next()...) if actually of type (Element1?, Element2?, ..., ElementN?) and this is not an optional that can be used in a guard statement. So maybe I'm missing something from the document or we might want to bless the ... operator with some special meaning regarding optionals (i.e. the result is nil if any element is nil, otherwise it is a tuple of non-optional elements).

If giving special meaning to ... for optionals is not desired, something like the following might be more appropriate:

switch (iterators.next()...) { // switching over a tuple of N optional elements
  case (.some(let v)...): // matching the case of all elements being non-`nil`. The variable `v` contains all the elements 
    return v
  default: // default case: at least one element is `nil`
    reachedEnd = true
    return nil
}

Tuple splatting

We could use the same ... operator for tuple splatting. Other than the example of the Manifesto, in which a function with variadic generics is called expanding the arguments passed to it, the feature might be useful to allow one to write something like this:

let (...head, tail) = someTuple
// `someTuple` is a tuple containing N elements
// `head` is a tuple containing the first N-1 elements of `someTuple`
// `tail` is a single value (not a tuple) containing the last element of `someTuple`
//
// If `someTuple` contains only one element, `head` will be the empty tuple and `tail` will contain such element
// This is not possibile in current Swift because single-element tuples are not allowed
//
// If `someTuple` is empty ww might want this code to not compile and raise an error

What do you think? Any thought about the implementation level of this? Let's start the discussion!

edit: link to previous discussion.


(Tomáš Znamenáček) #2

Did you read the previous threads about variadic generics? I got the impression that it’s an often discussed feature, so it would be great if new threads about it could also post a short summary of previous discussions. Otherwise we could easily repeat what has already been said.


#3

Good thinking! Here's a link to the other major thread, in which I link to a more minor thread.

This is a feature that's near and dear to my heart, so I'm definitely going to write something up once I'm off my phone and back onto a laptop.


(Andrea Tomarelli) #4

Yeah, I've seen the other thread but it was two and a half years old and I didn't feel to resurrect it a second time... I guess I should've linked it in my OP though, I'll edit it.