[swift-evolution] Variadic generics discussion

Austin Zheng austinzheng at gmail.com
Wed Jun 1 02:07:03 CDT 2016


> On May 31, 2016, at 9:22 PM, Brent Royal-Gordon <brent at architechies.com> wrote:
> 
> You know, I'm not a big fan of the `&` syntax people are pushing in the `Any<>` thread,

Finally, a kindred soul!

> but if you ignore the Ceylon people begging for union types, I think a sensible alternative meaning for `|` would be "all common supertypes of these types". Then you could write:
> 
> 	// Bottom here is the bottom type: the subtype of all types.
> 	(Int, String, Bool).reduce(Bottom, combine: |)
> 
> And you'll end up with something like:
> 
> 	Equatable & Hashable & _Reflectable
> 
> Which are the three protocols all of those types support. If they'd supported no common protocols, it would've ended up with `Any`. (Interestingly, this is arguably the same operation which should be used to determine the return value of tuple subscripting.)

This is great for a tuple whose types are known at compile-time, but if you were building a variadic generic type your variadic generic parameters would probably be constrained by the same sorts of type-equality, conformance, and subtyping relations that your normal parameters are today. In that case, it would be useful to have a notion of "this function's generic constraints for T" and "that function's generic constraints for U" line up such that T is a subtype of U, hence we have a useful, compile-time-knowable lower bound for the argument type of a function designed to reduce the members of a variadic generic tuple. You could use this same machinery to convert the bounds into a generalized existential, allowing for a non-generic reducer.

> 
> Of course, nothing about having this feature *requires* that we use `&` and `|`; that was just the inspiration for it.
> 
>> Other questions (inherent to any proposal) would be:
>> 
>> - How do we resolve the impedance mismatch between tuples and function argument lists? Is it even worth trying to resolve this mismatch, given that argument lists are intentionally not intended to mirror tuples?
> 
> As far as I know, tuples support a subset of argument list features. (For instance, they don't support default values.) So if you splat a tuple into a parameter list, the worst case is that you won't get to use features like those.
> 
> One mismatch is that tuples barely care about their argument labels, while parameter lists select overloads on them. My temptation would be to require you to disambiguate with `(x:y:z)` syntax if you were splatting in an unclear way.
> 
> 	let triple = (0, 10, 2)
> 	
> 	stride(triple...)		// Illegal due to ambiguity; write one of these:
> 	stride(from:to:by:)(triple...)
> 	stride(from:through:by:)(triple...)

I tested this - calling a function using the fully-qualified name works, but the argument list you provide still needs the parameter labels, which are redundant because they're part of the fully-qualified name. So you'd need: "stride(from:to:by:)(from:0, to:10, by: 2)". Removing this restriction would be a good first step.

Perhaps inout params of type T could be somehow passed as a pair (T, (T) -> ()), where the first item is the input value and the second item is a writeback function representing the 'out' part of inout.

I've no ideas for default values.

> 
> One useful thing here is that the arity and types of a tuple are known at compile time, so we can figure out ahead of time how to interpret the call. That wouldn't be the case if you could splat arrays.
> 
> (Incidentally, we might want to offer a pseudo-failable initializer to create a tuple from a sequence:
> 
> 	if let triple = (Int, Int, Int)(intArray) {
> 		return stride(from:to:by:)(triple...)
> 	}
> 
> That would indirectly allow you to splat the contents of an array into a parameter list.)
> 
>> - As you said, how do variadic generics work in the 0- and 1-member cases?
> 
> I don't think 0-tuples are a big deal, except in that there's no good starting value for certain reductions. Adding a bottom type (as I did in the `reduce` example above) would fix that.
> 
> 1-tuples are a bigger problem, because if you have a parameter like `(Any...)`, you can't afford to interpret `((Int, String))` as `(Int, String)`. It might be possible to support 1-tuples only in contexts where you're using a variadic tuple, but I'm kind of skeptical of that idea.
> 
> Personally, I wish we'd just acknowledge `(SomeType)` (or maybe `(SomeType, )`) as something different from `SomeType` and stop fretting. Sure, it's a semi-useless degenerate case, but so are one-case `enum`s and `Optional<Void>`s, and we don't ban those. Banning `T -> U` syntax has already removed one of the sources of ambiguity here.

I agree that 1-ples would make a lot of things cleaner. Did any of the proposals on the topic ever get to the PR stage?

> 
> -- 
> Brent Royal-Gordon
> Architechies
> 



More information about the swift-evolution mailing list