…the resulting compiler error is a bit misleading, as it implies you can have single-element tuples, just not with named elements.
Cannot create a single-element tuple with an element label
(with a fixit to just remove the name, but not the misleading parenthesis)
Reinforcing my point that single-element tuples, or rather Swift's aversion to them, is already odd. (I'm not critiquing it, just noting - I suspect there's an upside to disallowing them, such as removing confusion between tuple declarations and mere scoping, both of which use parenthesis… although they could be distinguishable by requiring single-element tuples to have a trailing comma)
The current behavior is consistent, in that the parentheses are just grouping and not tuple formation -- so (123) has the exact same type as 123, which is not a tuple type. All of the funny behaviors of tuples immediately follow from this syntactic overlap (and the subsequent decision to disallow one-element tuples instead of having a special (x,) or (_: x) syntax or whatever that has a distinct type from x).
For the sake of argument, another alternative would be to say that tuple members are suppressed from being looked up on types that appear to be scalar. We had the problem you describe with the .0 member back in the Swift 0.x days, where it would be present on every type since every type is a single-element tuple of itself, acting like the identity member because the first element of a 1-tuple is the entire value. Now .0 is only resolvable when a type is visibly a tuple in context.
Not much progress since the pitch sadly. The current state of the implementation is that the tuple conformance mechanism is already used for Copyable and Sendable tuples, and user-defined conformances make it as far as SILGen. The IRGen and runtime component still need to be implemented. If anyone wants to jump in I’d be happy to help figure it out.
The pitch only allows for (repeat each T) to conform to P where T: P, but with a bit of work we could also support more general tuple conformances.
A tuple conformance is essentially like an extension of a variadic nominal type:
struct FakeTuple<repeat each T> {}
extension FakeTuple ...
It's not supported right now, but we want this to work for parameter packs in general:
extension FakeTuple where (repeat each T) == (Int, String) {...}
So once that's worked out, your tuple conformance falls out immediately.
We have to be careful if we generalize this to avoid logical inconsistencies with one-element tuples; because a tuple conformance is just like the FakeTuple above, except that FakeTuple<T> is actually just T when T is a scalar type. We don’t want to back door a mechanism to make an arbitrary type conform to P because it was a one-element tuple before substitution.
You also wouldn’t be able to define two distinct tuple conformances to the same protocol, even with distinct requirements, because this would violate the restriction on overlapping conformances, just as if you tried the same with FakeTuple.
Also while it doesn’t create theoretical difficulties I’d prefer if we draw the line at adding arbitrary members to tuple types, and limit this to protocol conformances only. Otherwise overload resolution gets more complicated.
In that world, what's the rule for who's allowed to create tuple conformances to a protocol? For named types, we say it must be the module that either declares the type or declares the protocol. How does that work with tuples? Nobody owns a tuple type declaration.
The structural types notionally belong to the standard library, so only the standard library can non-@retroactive-ly define conformances for standard library protocols, and only the protocol's home module can non-@retroactive-ly define tuple conformances for a protocol outside of the standard library.
in Xcode 16.1 beta 1 and neither seems to get the following code to work:
public protocol TestProtocol {
}
extension Tuple: TestProtocol where repeat each Element: TestProtocol {
}
extension <each T> (repeat each Element): TestProtocol where repeat each Element: TestProtocol {
}
There's no IRGen or runtime support. I had some experimental code a while ago to stub out enough in IRGen to get it working, but this approach didn't backward deploy at all, requiring a runtime change, and it would be nice if we could get the stdlib tuple conformances to backward deploy, at least when you don't use dynamic casts.
Also, non-static methods in tuple conformances that actually pass a tuple as the self value hit some existing SILGen bugs with parameter packs, so don't get past type checking.
The only syntax that works is extension Tuple,but you need to actually declare typealias Tuple<each Element> = (repeat each Element). It won't get far though, as per the above.