The Generics Manifesto and single-element tuples

Sorry. I wasn't clear. My intent was narrow--merely contemplating a pitch that the tuple type include a single-element tuple, as it did previously. It seems to me that that would be a first step down the path. But...

I hear your caution, and accept is as prudence. Perhaps there is little point in addressing the single-element tuple until we have a better picture of how variadic generics will be handled.

I love it, but the trailing comma is not part of the current Swift syntax (it produces an error), so perhaps the parentheses syntax issue isn't really solved, yet.

@xwu, thank you for that assessment. It seems to me that we all could benefit from understanding what that "another model" is supposed to be, what its current state actually is, and the plan for moving forward, whether that be towards finishing the implementation or moving towards a different model. It feels like a bit of a mystery. Can you point us towards documentation or enlightening discussion threads?

1 Like

meaning, there is unambiguous syntax available if we ever want to make 1-tuples a thing again

:+1:

am i right in understanding that the problem lies with tuple nesting and not with 1-element tuples themselves? and the confusion is between a 1-tuple that could be interpreted as containing an N-tuple that contains the elements, and an N-tuple directly?

((T, U, V),) vs (T, U, V) // equivalent???

I think it's not inaccurate to say that, independent of implementation, the model intended today makes no distinction as to whether unlabeled 1-tuples exist or not. There is no feature of the language that turns on that distinction, so it is not made. Labeled 1-tuples are not intended to exist, and any situation in which it is possible to create one is a bug.

I don't think it's documented anywhere specifically. The discussion threads regarding SE-0110 are enlightening, as are discussions about removing tuple shuffling and changing the syntax surrounding enum associated values.

Would it be inaccurate to say:

  • Short answer: No, of course not.
    Longer version: 1-tuples does not exist in the intended model, because if they did, then these single element tuple types would be equivalent to their element type and thus every type T would be a(n unlabeled single element) tuple type, including eg Int, Void, [Int : Float?] and (Int, Bool, String).

?


A number of possible future language features (ExpressibleByTupleLiteral, enabling extension of tuple types, etc) become unnecessarily complicated to think and talk about if we are not sure about whether 1-tuples exist or not. It should suffice with a simple "yes" or "no".

1 Like

Again, I don't think the answer is currently either yes or no.

Do you mean that this inability to say if 1-tuples exist or not is a desired property of the intended model?

Neither. Let me use an analogy. There are three answers to the question of whether someone believes in a deity: you can affirm, you can deny, or you can be agnostic. But those answers don't really apply to the question of whether someone believes in a teapot circling the universe. You can't even really be agnostic; it's simply not a question that's contemplated.

Similarly, the existence of 1-tuples is not within the set of questions that's contemplated by the language. You can argue that we ought to contemplate it now and decide on an answer; however, if your question is whether (prior to now) 1-tuples exist or do not exist, or whether the language is deliberately agnostic to the answer, that is unanswerable.

Let me make a rough summary of this thread.

My original post ends with the following question:


The Language Reference is pretty clear IMHO that 1-tuples don't exist:

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


@Tino and @taylorswift says it's weird that they don't exist:


@Joe_Groff says they do exist and are equivalent to the type of their element:


@xwu says that we cannot even be agnostic about their existence:


I think it would be nice to decide on an answer, yes : ).

Because until we do, we can't reason and communicate about matters related to tuple types (especially for example ExpressibleByTupleLiteral, enabling extension of tuple types and variadic generics), as demonstrated by this thread.

My current understanding (of the relevant part) of the intended tuple model is this:

  • – Does 0-tuples exist?
    – Yes

  • – Does 1-tuples exist?
    – There currently is no answer, but the type of a 1-tuple is probably equivalent to the type of its element.
    – Hmm, OK, so all types are probably tuple types then? That is, both String and (a: Bool, b: Float, c: String) are examples of unlabeled single element tuple types … :thinking:

  • – Does 2-tuples exist?
    – Yes, there are 2-tuples, 3-tuples, 4-tuples, and so on (and their type is separate from, but decided by the types of their elements and their order).


If it turns out that the question ("does 1-tuples exist?") truly is irrelevant, then I think that we should decide on a clear explanation of the current model.

This explanation must succeed in making the reader see that that question is obviously irrelevant, because I'm probably not the only one who currently sees that question as relevant, or even fundamental, for understanding and talking about Swift's tuples.

2 Likes

instead of all these debates about teapots and deities how about we stop and think about what this means for the things we actually want to use them for.

I wrote a function that takes three arguments.

func foo1(a:Int, b:Int, c:Int)

Can I call it like this?

foo1(1, 2, 3)            // currently doesn’t work

foo1((1, 2, 3))          // currently doesn’t work

foo1(a: 1, b: 2, c: 3)   // ✅

foo1((a: 1, b: 2, c: 3)) // currently doesn’t work

I wrote a function that takes a single tuple argument with 3 elements.

func foo2(_ arg:(a:Int, b:Int, c:Int))

Can I call it like this?

foo2(1, 2, 3)            // currently doesn’t work

foo2((1, 2, 3))          // ✅

foo2(a: 1, b: 2, c: 3)   // currently doesn’t work

foo2((a: 1, b: 2, c: 3)) // ✅

What if the argument label is required at the call site?

func foo3(arg:(a:Int, b:Int, c:Int))

Can I call it like this?

foo3(arg: (1, 2, 3))            // ✅

foo3(arg: ((1, 2, 3)))          // ✅

foo3(arg: (a: 1, b: 2, c: 3))   // ✅

foo3(arg: ((a: 1, b: 2, c: 3))) // ✅

Should this be allowed?

func foo4(arg:(a:Int)) // currently doesn’t work

Can I call it like this?

foo4(arg: 1)        // according to foo3, this should work

foo4(arg: (1))      // according to foo3, this should work

foo4(arg: (a: 1))   // according to foo3, this should work

foo4(arg: ((a: 1))) // according to foo3, this should work

What about this?

func foo5(_ arg:(a:Int)) // currently doesn’t work

Can I call it like this?

foo5(1)      // according to foo3, this should work. according to foo2, it shouldn’t

foo5((1))    // this should work, but doesn’t.

foo5(a: 1)   // this shouldn’t work.

foo5((a: 1)) // this should work, but doesn’t.

Should this be allowed?

let a:Int = (b: 35) // currently doesn’t work

What about this?

let a:(b:Int) = (b: 35) // currently doesn’t work

If I have this:

let a:Int = 13
let b     = a

what is the type of b? is it Int or (a:Int)?


If I have this:

let a = (c: 13)
let b = a

what is the type of b now? Int, (a:Int), or (c:Int)?


I want to write a variadic vector addition function that can also take a scalar, so that I can add Ints, (Int, Int)s, (Int, Int, Int)s, and so forth. Should I be able to do this?


I want to make the above function generic, so that I can add Ts, (T, T)s, (T, T, T)s, and so forth. Should I be able to do this?


I want to do the same thing as above, but allow T itself to be a 3-tuple type. If I add two T values, am I adding them as two 3-vectors, or two scalars?

6 Likes

I agree that the solution isn't to write off the intended design as flawed. But discussions such as this, and also bug reporting and fixing, would probably be more productive if the intended model/design was clearly documented, including any relationship among tuples, parenthesized expressions, parameter lists, etc. Such a document should also make it clear if/how the model makes questions like "Does 1-tuples exist?" unanswerable/irrelevant.

Can someone more experienced than me please start a rough sketch?
(I suppose it would end up here as eg Tuples.md.)

This document could then be announced in a separate thread, and discussions like this could continue (more productively) there, while establishing a common understanding by referring to and improving the document.

1 Like

@xwu, thank you for pointing me towards some of the history. Oh, man, what a story it is.

I was vaguely aware of the issue surrounding the implementation of SE-0110, but not the details or the extent of how it all started, how it ended up in trouble, and where it stands, now. If one were to look for technical debt related to the current state of tuples and parentheses, there it is. I feel bad for those guys--trying to make changes in those parts of the compiler seems fraught with hidden perils. :face_with_head_bandage:

Having recently spent a substantial amount of time tracing through parser and type-checker code to debug a tiny parentheses/tuple-related issue, I've got to say that I would love to see a summary of the current principles, rules, algorithms, etc., governing the parsing and type-checking of parentheses, tuples, function signatures, and the like. I don't suppose anyone might have a set of notes sitting in a drawer? I could start at main and walk through it from beginning to end, but where to find the time...

3 Likes