Roadmap for and state of single element tuple types?

Semantically arrays are different than tuples. Tuple's are what models most of our type system and the flattening is for convenience to remove behaviour that would otherwise be an irritation to work with. Unlike arrays where one would be surprised if it were flattened.

I don't think single tuples are banned they're just sugared from (T) into T and vice versa. The same way (5) == 5.

I guess my mental model, as mentioned above, alleviates concerns I would've had for this behaviour.

To clarify my position I'm not against having a single element tuple. Just as long as it doesn't change any current behaviour and doesn't add more syntactic weight. Someway of building a single element from current language syntax like Tuple<T> would be ideal IMHO.

func f() -> Tuple<Int> {...}
let a: Tuple = f() // (Int)

let a = (Tuple<Int>(5), 2) // ((Int), Int)

I don't think single tuples are banned they're just sugared from (T) into T and vice versa. The same way (5) == 5.

Just to pick up on one small point: under the proposed model, (5) would not be a tuple; you'd need to explicitly say (5, ) (or (5)., or Tuple<Int>(5), whatever is decided upon).

(I wrote fixed size arrays, and edited to add type level count, in order to make the analogy more relevant, but let's go with regular arrays.)

Sorry for repeating this but it's so easy to forget: Parentheses are unfortunately used for several entirely different purposes, two of which are:

1. To group parts of an algebraic expression, as in (x + 2) * y. Here, they are used to induce order of operations by overriding normal operator precedence. It's easy to see how and why parentheses when used in this sense can be superfluous: The order of one or zero things (operations) always stays the same, so the following parens have no effect: (1 + (2)). Parentheses (when used in this sense) can also be superfluous in that the precedence of the (2 or more) operators are such that the order isn't changed by the parentheses, as in eg: 1 + (2 * 3). The convention is to allow such superfluous parentheses. They don't change anything, and can be used to increase clarity (for human readers).

2. To identify tuple elements, as in
let tuple: (UInt8, (String, Float)) = (1, ("two", .pi)). Here parentheses are used to identify the elements of a tuple type or literal, just like square brackets are used to identify the elements of an array. But tuple types can have a different type for each element, and the elements can have labels, and the number of elements are at the type level.

Oh, and tuples can have 0 or 2 or more elements, not 0 or more like arrays.
– But why can't they have one element?
– Well, ...

... because ...

... in Swift's early days we thought it would be possible to use tuples (including single element ones) for almost everything, but then we realized that all aspects of parameter lists (inout, @noescape etc) cannot be represented as a tuple, and since we use parentheses for a lot of things, we sometimes conflate these things, and it gets confusing, and it's kind of hard to communicate with all these different things dressed up as parentheses, but at least we solved the parameter list / function type issue by always requiring explicit parameter-list-parens but ... long story short: We've had and still have a lot of confusing discussions, bugs and inconsistencies related to tuples, argument lists, pattern matching, almost anything having to do with parentheses, and banning single element tuples seemed to solve at least some of the problems, and it's also for convenience, at least in some cases, even though it's not so great for consistency, and it does imply some other weird inconsistencies and needless limitations (for eg variadic generics). Also ((1) + (((2) * 3))) is the same as 1 + 2 * 3 so why shouldn't ((Int)) be the same as Int? After all, A product of types is a direct product of two or more types, so we could have used an infix * instead of parens to indicate tuples, and Haskell has no 1-tuples, at least not in their parenthesized tuple syntax.

Another big difference between the semantics of tuples and eg arrays:

I've had no trouble understanding or using Swift's arrays, their behavior seems to allow me to easily form a working mental model, the rules are few and consistent, no strange exceptions, very few things to keep in my head, I've never been surprised or frustrated by them, or worried about their design, implementation and effect on other parts of the language.

But with tuples, it's been quite the opposite for as long as I can remember. My mental model of them are a tangled mess, which might be somewhat in line with their implementation in various versions and parts of the compiler. And what the language reference says about them doesn't make much sense to me and is demonstratably not true in a lot of cases.

I'm not sure I understand what you mean, could you please clarify?

Just as a thought experiment, assuming Swift's tuples were written using eg ⟪…⟫ instead of (…), and ⟪⟪Int⟫⟫, ⟪Int⟫ and Int were three different types, and only the first two tuple types. Would there still be such irritating behaviour, and if so, can you give an example?

I guess it is possible that Int is considered a no-label 1-tuple type, and I'd like to know if this is the case, and what practical/technical reasons and consequences it has. Here's what the Swift Book has to say about it:

All tuple types contain two or more types, except for Void which is a type alias for the empty tuple type, ().

And in Xcode 10 beta and later, we have this situation:

// we can instantiate and use labeled 1-tuples:
let a = (label: 123)
print(a.label + a.label) // 246

// but not if we specify the type explicitly:
let b: (label: Int) = (label: 123) // Error: Cannot create a single-element tuple with an element label

But it's the special casing of 1-tuples (no matter if they are "banned" or otherwise special) that I think is problematic. Special casing spreads as in @Torust's example.

It seems to me like you're just accepting this loss in expressivity and uniformity that comes from special casing 1-tuples (which in turn afaics might not even have been deemed necessary if tuples hadn't been written using parentheses).

I think it would be great if we could find some way of not special casing 1-tuples, syntactically or otherwise, and which also made all tuple expressions (not just 1-tuples) clearly distinct from parenthesized expressions, just to get rid of all potential confusion.

But if Int has to be a tuple type for some reason, then I don't know, and I'd like to know if Int is both a basic primitive type and a compound type?

Regarding not changing current behavior, here are some examples of the current behavior:

// This program compiles with Xcode 9.4 and Xcode 10 beta:
let t = (label: 12).label + (label: 34).label
print(t) // 46

// This program compiles with Xcode 9.4 and Xcode 10 beta:
let t = (x: "first", x: "second")
let (a, x: b) = t
print(a, b) // prints: second first

// This program compiles only with Xcode 10 beta and recent snapshots:
let t = (label : 123)
// We've just defined a single element tuple, and we can use it as such:
print(t.label) // prints: 123
switch t {
  case (label: 123): print("It's 123") // <-- Will match this case
  case let (label: v): print(v)
let (label: v) = t
print(v) // prints: 123

// This program compiles with Xcode 9.4 and Xcode 10 beta:
let `true` = !(true: true).true // Drop .true to crash compiler!
print(`true` == false) // prints true

Regarding syntax:
I neither like (1). (maybe due to memories of Pascal ;-), nor (1,) (trailing commas just look odd).
My preference would be (:1) - if there shouldn't be a label, just leave it out.

But afaics, there would be no ambiguity on the declaration site, and as single element unlabeled tuples probably won't be used often (after all, we don't actually need them ;-), I could live without any special syntax

let x = (5) // this is just an int
let y: (Int) = (5) // this is a tuple

What about:

let x = (5) as (Int) // ?
let y = 5 as (Int) // ???

And maybe

let z: (Int, Int, Int) = 1, 2, 3


This opinion might be an exception in this discussion, but I wouldn't mind restoring the old model, where unlabelled 1-tuples are equivalent to the type ((Int) === Int), with the difference that the contraversial .0 member (which just returns self) is disabled.

(Or perhaps we could keep .0: there was recent discussion about finding an identity keypath, this could be a good candidate, since it can't be shadowed)

This model nicely avoids the semantic issue of bracketing vs tuple creation, since (4 + 4) * 2 defines the same order of operations, and is semantically correct, for either mental model.

Given the new conversion rules noted in the opening post, I would expect this to be compiler friendly, and it seems user-friendly, too.

I'm simply referring to the original system that's diverged only due to the need for function parameter attributes that couldn't be represented in tuples themselves. But you're already aware of this so I won't elaborate over it too much. But even though it's diverged a little in one place it's still prevalent everywhere else.

Yes, exactly, this is the behaviour I'm advocating for (which is the current one).

No there wouldn't be. But we don't have "⟪⟫" or something else and there's nothing we can do about that now. We have parenthesis and the current behaviour is the most consistent behaviour we can have due to this IMO. See the relation between how expressions are reduced and how tuples are as well.

(5 + 5) * 2 == 20 // ('Int' + 'Int') * 'Int' == 'Int'
((5 + 5) * 2, (10)) == (20, 10)  // (('Int' + 'Int') * 'Int', ('Int')) == ('Int', 'Int')

Note, I only meant for expression evaluation and type inference, sorry if I wasn't clear.

I don't see a problem here. You could also do this with structs or classes:

let t = Box(label: 12).label + Box(label: 34).label
print(t) // 46

let t = (x: ..., x: ...) should be an ambiguity bug IMO, regardless of being able to use .# as mentioned in the other thread.

let (a, x: b) = t would not be a problem if the above is fixed as it is only a consequence.

I think the fact that compiler crashes should be enough to say this is a bug :slight_smile:. I imagine the program shouldn't compile when .true is droppped. Since (true: Bool) != Bool and ! is not defined for it.

Most of the inconsistencies you mention are with dealing with labelled single element tuples. They are in my opinion are just bugs that need fixing.


Perhaps these particular examples are, but there are many other bugs and unfortunate circumstances (like the above example about the effect implicit construction/deconstruction/conversion or same-ness of ⟪T⟫ and T will have on variadic generics, enum with associated values, pattern matching, etc, etc), which have probably more to do with the layers of confusion resulting from having parentheses play all these multiple roles.

But ... if we accept that ⟪Int⟫ is the same as Int, then why isnt't Box<Int> the same type as Int? Why should generic structs or classes be allowed to have a single type parameter, aren't they product types too?
: )

Also, let's remember that the following two examples (which you have no problem with) are accepts-invalid bugs according to the language reference / TSPL, which says: "All tuple types contain two or more types, except for Void which is a type alias for the empty tuple type, ().":

// This program compiles with Xcode 9.4 and Xcode 10 beta:
let t = (label: 12).label + (label: 34).label 
print(t) // 46

// This program compiles only with Xcode 10 beta and recent snapshots:
let t = (label : 123)
// We've just defined a single element tuple, and we can use it as such:
print(t.label) // prints: 123
switch t {
  case (label: 123): print("It's 123") // <-- Will match this case
  case let (label: v): print(v)
let (label: v) = t
print(v) // prints: 123

and they are also accepts-invalid bugs according to this error:

let b: (label: Int) = (label: 123) // Error: Cannot create a single-element tuple with an element label

So, as I've said before, until we have some common understanding and official declaration of the intentional design, reporting and fixing(?) such bugs(?) will probably only create more confusion.

This is the crux of the problem imo, and it's a pity if we really can't do anything about it.

Because then we have to live with serious problems (inconsistencies, confusion, limited expressiveness) caused by something as mundane as the choice of notation / which characters to use to indicate tuple elements.

I really don't see the issue with variadic generics, in all honesty, maybe I just don't understand what the point @Torust was saying. We don't know what the specific design of variadics in Swift will be. But from my understanding, the concern has to do with the return type being a tuple for everything except when there's only one element then it's not a tuple. But again I don't see why this is bad, maybe I'll need to use it in practice to see the issues it will cause.

enum with associated values, pattern matching are separate and were handled with SE-0155, which is not fully implemented, hence the new thread dealing with source compatibility issues regarding the full implementation of the proposal in today's Swift.

Box isn't generic. I'm simply pointing out that you can instantiate an object and get its property the same way you would for a tuple. Which I'm assuming was the point you were showing?

I realise now it's misleading to use Box due to it being a bit of a term of art. A physical cardboard box that has a sticker label was my thinking when writing this example :)

Right, so we've both been speaking with different assumptions. My assumption was that the documentation has a bug and yours is the compiler has the bugs. My thinking was that this is just a limitation of not being able to represent single element tuples and not a design decision. Hence to support it one would not need a proposal and the feature can just be introduced, though I may be wrong in thinking this.

Well, it would be source breaking, so you'd have to show active harm. I do see the argument for confusion, but not so much harm.

I think that, in order to have a meaningful discussion about tuples, we first have to know whether it is 1 or 2 that is the case in the following.

Assuming the current situation, that ((T)) == (T) == T, then either:

  1. All types are tuple types (1-tuples)


  1. There exist no such thing as a 1-tuple.

As far as I understand, these are the only two possibilities (please correct me if I'm wrong).

The difference between 1 and 2 might perhaps seem philosophical and without practical consequences, but here's an example of how it is not:

It doesn't seem unlikely that structural types (like tuple types) will one day allow extensions and protocol conformances.

So, once we can extend tuple types, something like this:

extension Tuple {
    func foo() { ... }

then, depending on whether 1 or 2 is the case:

  1. All types will have foo().


  1. Only (), (T, U), (T, U, V), ... will have foo().

I don't know which of these two makes most sense, but I whish the situation wasn't such that we must logically rule out a third (imo more intuitive) option, ie:

  1. Only (), (T), (T, U), (T, U, V), ... will have foo().

(But as long as ((T)) == (T) == T, case 3 is the same as case 1.)


This is expressly disallowed from the generics manifesto/Extensions of structural types. To quote the specific part:

There are some natural bounds here: one would need to have actual structural types. One would not be able  to extend every type:

extension<T> T { // error: neither a structural nor a nominal type

I'm not sure how 2 would work with the extension you defined. Could you rephrase using the notation from the manifesto?

Unless the Generics Manifesto disallows a protocol to which all tuple types conform, you can imagine Tuple in my example being that protocol. (Does the manifesto disallow such a protocol?)

Here is an attempt to rephrase my code example (without any such protocol and) using the notation from the manifesto:

extension<...Elements> (Elements...) {   // extending the tuple type "(Elements...)"
    func foo() { ... }

I'm not sure what the manifesto says about that, but my guess is that it's considered to be equivalent to:

extension<T> T { // error: neither a structural nor a nominal type

But is it really?

Anyhow, so far this points towards case 1 being the case.

Furthermore, the Generics Manifesto seems to be confident that the number of tuple elements is zero or more (not zero or two or more).

As can be clearly seen in this example from the manifesto.
public struct ZipIterator<... Iterators : IteratorProtocol> : Iterator {  // zero or more type parameters, each of which conforms to IteratorProtocol
  public typealias Element = (Iterators.Element...)                       // a tuple containing the element types of each iterator in Iterators

  var (...iterators): (Iterators...)    // zero or more stored properties, one for each type in Iterators
  var reachedEnd = false

  public mutating func next() -> Element? {
    if reachedEnd { return nil }

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

    return values

So I guess this means that the answer to my question is that:

  • Case 1 is the case, ie: All types T are tuple types, since T == (T). Except eg unconstrained generic type parameters, which are "neither structural nor nominal types". (Which makes me wonder if there are any other types that are not tuple types?) Perhaps a more correct formulation could be:
    "All nominal and structural types T are tuple types, since T == (T)"? (And this seems to imply that all nominal types are also structural, since tuple types are structural ... Could someone who knows this stuff please explain, @Douglas_Gregor ?)

So according to my interpretation of the manifesto, the language reference is wrong when it says:

A tuple type is a comma-separated list of types, enclosed in parentheses.

This is not true, because eg Int, which is the same as (Int), is also a tuple type, as is eg [Float?], none of which is a comma-separated list of types. And it's also wrong when it says:

All tuple types contain two or more types, except for Void which is a type alias for the empty tuple type, ().

This is not true, because a tuple type can contain zero or more elements.

Even the grammar is wrong:


tuple-type              → ( ) | ( tuple-type-element , tuple-type-element-list )
tuple-type-element-list → tuple-type-element | tuple-type-element , tuple-type-element-list
tuple-type-element      → element-name type-annotation | type
element-name            → identifier

How can The Language Reference and The Generics Manifesto be in such fundamental disagreement?

A week later.
I'm getting slightly worried that there is no common understanding (even among compiler devs) of Swift's tuple types. So I'll try again to get a definitive answer on at least this question:

Should these two programs compile or not?

Program 1:

let a = (label: 123)
print(a.label + a.label)

Program 2:

let a: (label: Int) = (label: 123)
print(a.label + a.label)

(Clearly, both programs should either compile or fail to compile. With Xcode 9.4, both fail to compile, but with the default toolchains of Xcode 10 beta 1 and 2, as well as with recent dev snapshots (eg 2018-06-24), Program 1 compiles.)


I asked about this on Twitter and it sounds like neither of those programs should compile. As I understand it, single-element tuples are currently only allowed due to implementation quirks and should be banned altogether.

When this is eventually (hopefully) sorted out at the implementation level, I'd be in favour of a proposal to introduce single-element tuples with some kind of special identifier syntax. At this stage, though, such a proposal sounds like it would be a recipe for bugs.

1 Like

Thanks! OK, though I'm still confused, I've filed:
SR-8109 - Swift 4.2 Regression: Single-element tuple not prevented

I agree, but I would prefer if the syntax for tuples (in general, not just single-element tuples) could be entirely separated from other concepts in the language that happens to be written using parentheses.

And I still welcome any clarifications to the questions raised upthread.

1 Like

Regarding this thread, as well as Jens' July 2016 posts, about parentheses, tuples, etc.: Jens' patience and enthusiasm for this topic are remarkable, and his writing on the subject is excellent. Thank you, Jens. Also, kudos to Letan and Thomas for their efforts and sharp counterpoints. Great discussion, all around.

These issues are a big deal. No matter their outcome, they have real implications for the long-run development of the Swift language.

I would be glad to see (or, if need be, help to create) a top-down decision tree for how parentheses are parsed and type checked by the compiler. I suspect that the parser and type checker are missing subtle opportunities to infer the intent of parentheses from context. I also suspect that there are edge cases where inference is impossible. Airing that sort of document would show us the specific problems, inform the discussion, and also might lead to creative solutions from the community.

The compiler has been built and modified over a long period of time by a lot of people. My impression is that, out of practical necessity, many modifications have been made as local patches rather than systemic integrations. At this stage, any real change to how parentheses are handled probably involves a frightening amount of retooling within the compiler. The work and the attendant risks of error are daunting, but waiting has its own costs. Whatever happens, great respect is due to the core team; they do amazing work.

Please see the commentary on SR-8089. In particular, there may be some significant under-the-hood work on tuples in the near future. My supposition is that that work may clear the way for better discussions about above-the-hood aspects of the issues addressed by this thread.

Let's find a way to keep this thread near the top of the heap.


To circle back on this: single-element labeled tuples are now banned: Ban one element tuples harder by slavapestov · Pull Request #21343 · apple/swift · GitHub

Note that in SR-8089, you're observing "parenthesis types", which are just sugar. (T) is equivalent T and does not designate a tuple type.


Ah, I see that it fixed now, nice.

I'd like to echo a question I wrote in a comment to SR-8172 here:

Are there any situations where a user would benefit from being able to observe a "parenthesis type"? Isn't it always just a pointless and confusing exposure/leakage of compiler-implementation details?

Also, why should Swift even allow us to write:
let x: (Int) = 123
in the absence of single element tuples?

To me, the above makes as little sense as:
(let) x: Int = 123

I use parentheses when the return type is a function because it helps me to read the thing: (T) -> ((T) -> U). Swift should definitely allow me to write that.

If arbitrary many other keywords could be used before and after let, and if surrounding them with parentheses in certain situations were required, then yes I would also expect Swift to allow me to write non-required parentheses around let as well.