The Generics Manifesto and single-element tuples

The linked example would work fine in the model as I've described it. a is contextually of type UnsafeMutablePointer<Int> so the single-value reallocate would be chosen, parens or not, tuple or not.

For another example, let's say we had a variadic dual to print to read a bunch of values from stdin:

func scan<T...>(_ types: T.Type...) -> (T...)

If you called scan(Int.self), you would expect a single Int back, not a tuple.

1 Like

Thanks again, I really appreciate you taking the time to answer these questions.

I mentioned that bug as an example because of the conversation (between @mattrips and @beccadax) in its comments, eg:

I'm afraid that I'd be putting a band-aid on a small symptom of a larger bug. The code that folds (T) is doing something that is fundamentally illegal.
It is creating a single-element tuple, but there is no such thing as a single-element tuple. The documentation states that, "All tuple types contain two or more types, except for Void which is a type alias for the empty tuple type, ()."

Internally, Swift actually does use single-element tuples purposefully in some cases (mainly for single labeled arguments). But we know they sneak through sometimes when they shouldn't. The set of refactorings needed to fix it properly is too big to do immediately, but several of us are gearing up to wage war on the root causes of these problems.
So apply your band-aid and bide your time. Honestly, if it actually fixes a single-element tuple instead of just limiting the damage, it's better than the tuple band-aid I wrote a couple weeks ago.

And also because of the content of the reporter's (also assignee's) related post here.

You're right, but I'd prefer if the implicit splat wasn't there (and thus that the 1-tuple's type was separate from its element), since it would allow me to eg write extensions on all tuple types for which all (0 or more) elements satisfies some constraint etc. But as shown above, the current special casing of 1-tuples makes that complicated or impossible in the general case (the current special casing results in the need for further special casing). It would be interesting to hear if @Torust has any thoughts about this feedback.

I see your point, but again, I still don't like the special casing of 1-tuples (having an implicit tuple splat), even here, because it's trading consistency (and the power/simplicity it implies) for mild convenience in a specific case (by introducing an inconsistency and the trouble/complexity it implies).

I'm not sure if it's interesting or not to contemplate the behavior of the following depending on if 1-tuples existed as separate types from their element's type or not:

func foo<...T>(_ types: T.Type...) {
    // Or would it be:
    // print(scan(T...))
    // ?

I'd be happy to come to the conclusion that my worries and/or preferences are founded on false premises, but I'm afraid I seem to need a lot of explanation in order to arrive at that conclusion.
: )

1 Like

[Not necessarily directed at you, Joe] For me, the question still stands: As a matter of formality, why doesn't the single-element tuple exist in Swift?

And, by "in Swift", I mean at the source-code/user level, not internal to the compiler. Regarding that distinction, please see SR-8172.

1 Like

Likewise, thanks for having patience with my explanations. I know there's a lot of implementation mess and technical debt to fight through that's obscuring the model Swift really intends to implement.

This is where the difference in generic context comes into play. A single type can be both itself in a nongeneric context and a list of one type in a variadic generic one. In the body of foo, we know types could have any number of elements, so it would always be worked with as a tuple.


I agree.

1. Removing exceptions has a value by itself, and can often add power to the language by simplifying its components while at the same time allowing for better compositionality.

2. Introducing exceptions motivated only by small wins in specific use cases, in acts of unexamined pseudo-pragmatism, probably always adds more complexity and confusion than power to the language.

I guess I'm worried that the issues at hand (implicit tuple splat of the existing-yet-non-existing-1-tuples, the implication that all types are tuple types, uncertainty on how and when various different cases of variadic generics should be special cased, messy implementation, etc.) might be symptoms of Swift Evolution having been doing too much of 2 and too little of 1 (on top of conflating different concepts all dressed up as parentheses).

I also guess that many will dismiss this view as naive idealism or, if I manage to conceal my lack of actual knowledge well enough, too academic.

Yes, and AFAICS both variants of the print statement would be valid code, as would this:

func foo<...T>(_ types: T.Type...) {
    print("A:", scan(types))
    print("B:", scan(T...))

It would be interesting to see the behavior of eg

foo(Int.self, Bool.self)
foo(Int.self, Bool.self, String.self)

for these two different models for tuples:

  • The type of a 1-tuple is equivalent to the type of its element (the current/intended one).


  • The type of a 1-tuple is separate from the type of its element, just like the type of any other N-tuple (the IMO more consistent model that I'd prefer, assuming solved conflated-parens-issue).

I'm too tired to try and figure that out though.

I'm pretty sure this discussion is grounded in the real world. In fact, this thread started with Generic Manifesto's proposed real-world use of single-element tuples. Consider:

2+2, 3+1, 10-6, and 4, each can be equated to the same value, but each models a different real-world event. They are different things.

T and (T), each can be equated to the same MetaType, but each models a different real-programming-world event. They are different things.

I don't recall any of these matters being decided by evolution proposals, so I think your blame might be misplaced here. There were proposals to try to deal with certain aspects around tuples, particularly SE-0029 and SE-0110, but moving away from modelling parameter lists as tuples, banning single-element tuples, etc were all done outside of, and generally before, the evolution process. I remember that one of the earliest Swift bugs that I reported, before it was an open source project, was that the removal of labelled single-element tuples broke the ability to label enum cases with a single piece of associated data.


Right, I should have written just "Swift" instead of "Swift Evolution" (but I'd still be happy to see Swift Evolution doing a bit more of 1).

Not super important, but ExpressibleByTupleLiteral and a library level way to destructure values into tuples would be "nice to have" for Python interop.



To add on to that, if we remodel tuples in terms of a specially-treated Tuple<T...> type, we would have a natural syntax for expressing extensions and conditional conformances on tuples:

extension Tuple<T...>: Equatable where T: Equatable { ...

This absolves the need to define new grammar to support (T...) in the extension declaration.

That’s not actually the syntax we use for extensions on generic types. Do you mean extension Tuple: Equatable?

1 Like

Ah, I wasn't clear. I was presupposing the implementation of the syntax at the end of the section on Parameterized Extensions. Although, I didn't quite get that right, either :stuck_out_tongue:

I'm not sure how such a model would handle the case where T... is an empty list, ie could there be an empty Tuple<> that is different from Tuple<Void>?

This problem does not exist in the current ~corresponding formulation from the generics manifesto:

extension<...Elements : Equatable> (Elements...) : Equatable {   // extending the tuple type "(Elements...)" to be Equatable

(Disregarding the question about whether all the elements of an empty list of types could be considered as conforming to Equatable.)

And I guess, we'd also have to ask ourselves whether we should special case single element Tuple<T> types or not, that is: If eg the type Tuple<Int> should be allowed and/or be equivalent to Int.

IMO the obvious answer is that there should be no special casing, ie the type Tuple<Int> should be allowed and not be equivalent to Int.

It is for the same reason that I think Swift should not special case single element tuple types like it currently does. However, that would require fixing the parentheses issue (the conflation of different concepts caused by overloaded parentheses).

I think the type should be Tuple<...T> : ExpressibleByTupleLiteral instead of Tuple<T...>. The only issue I see is the empty tuple which would require some special casing. ExpressibleByTupleLiteral would always produce a tuple of at least one element. Furthermore we'd need variadic optional labels for each type.


struct Tuple<...label?: ...T> : ExpressibleByTupleLiteral

The extension can be very similar:

extension Tuple : Equatable where ...T : Equatable { ... }

We can also drop the following special case T == (T) or T == Tuple<T> and make it T != Tuple<T>. Furthermore we can easily have Tuple<label: T> types or if we really want (label: T).

@Jens, to clear the way for these sorts of potential improvements and others, perhaps it is time to start formulating a pitch to revise the definition of the tuple type.

1 Like

My feeling is that it'd be premature to propose Tuple<T...> or ExpressibleByTupleLiteral now. Without variadic generics, neither feature would be adequately expressive in the language today, and the concerns raised in this thread are largely theoretical rather than practical without variadics.

My own experience doing variadic generic programming in C++ and the language I worked on previous to Swift, which had variadics and a distinction between Tuple<T> and T, was that it was a major pain for expressive variadic APIs, since it was almost always necessary to either special-case the unary case not to produce a tuple or do goofy ad-hoc testing to try to accommodate either a scalar or one-element tuple uniformly (which would then break when you want the tuple to be preserved). Swift's parametric generics system and tuple design gives us a great opportunity to completely obviate this concern, and I think it's premature to throw that away.


y’all need to stop getting so hung up on the parentheses syntax issue. this is a solved problem. just add a trailing comma before the ) to differentiate.

The original question in this thread hasn't ever been answered because, essentially, it's unanswerable. Swift had one model of the relationship among parenthesized expressions, tuples, and parameter lists, but this was abandoned as unworkable. Now it has another model, but this has not been completely implemented. Difficulties arise from the fact that there is no complete implementation of any one design. The solution isn't to write off the design as flawed and to propose another one: that's how you get three incompletely implemented designs.


Perhaps at least the following simple question (which I think has never been clearly answered) can be answered:

According to the model Swift intends, in the Swift language (not in its impl), does 1-tuples exist?

(I assume the answer must be “no”, because otherwise all types would be tuple types, since T == (T).)