i’m not an expert on this but it seems ExpressibleByTupleLiteral would also need a lot more Builtin magic than most of the existing protocols. A Swift integer literal is always a 1024-bit integer value (or was it 2048?) but a tuple literal can be anything.
I think the ABI stability aspect is just one factor; the overall use of protocols for literals is something I've personally been weighing in my head for a while. If we were going to make tuple syntax user-extensible, I think that also strains the protocol-oriented model at a type system level past a reasonable threshold. To be clear, this is all also Just My Opinion, not official by any means.
Work with them enough and you realize they aren’t elegant at all. If we’re being honest, the ultimate end goal for user-customizable literal conformances ought to be to make them integrate well with static asserts and @compilerEvaluable, but ExpressibleBy protocols don’t even let us do that, since it’s possible to call the inits dynamically, even though there is no reason anyone would ever want to.
In Swift 5 we have a new fancy Builtin.IntLiteral rather than Builtin.Int2048
Integer literals are arbitrary-precision as of Swift 5; we got that in before locking down the ABI.
Edit: beaten :)
For @tupleLiteral, you mentioned that the init signature would be the tuple literal, but it might be a little inconsistent with the other attributes for those being the argument for the designated initializer. i.e. :
// integer literal
struct Integer {
// Notice that the literal value is the argument
@integerLiteral
init(integerLiteral value: Int) {}
}
let int: Integer = 0
// tuple literal
struct X {
// Literal value here is the initializer itself
@tupleLiteral
init(y: Int, z: Bool) {}
}
let x: X = (y: 0, z: true)
// vs.
struct X {
// Tuple literal is the argument
@tupleLiteral
init(tupleLiteral value: (y: Int, z: Bool)) {}
}
let x: X = (y: 0, z: true)
Also, @tupleLiteral initializers would be kind of awkward with types that have a single argument (single argument tuples with labels are banned):
// Silly example, but maybe there are use cases
// for a single argument tuples
struct BoolWrapper {
@tupleLiteral
init(bool: Bool) {}
}
// error: cannot create a single-element tuple
// with an element label
let boolThing: BoolWrapper = (bool: true)
These are just some design thoughts I had after the discussion because I might be interested in toying around with this when I get the chance.
could we just ban it for 1-argument inits?
I don't see any reason why the constraint on tuple types has to apply to types that only use the tuple literal syntax.
Why wouldn't we make this match the other literals in the system? I should be able to do:
struct MyThing : TupleLiteralConvertible {
init(tupleLiteral value: (Int, Int)) {}
}
var x : MyThing = (4, 2)
So yeah, we need variadic generics to make this particularly useful, but we need that anyway.
-Chris
I have had some similar thoughts as well, but after the discussion during the review for SE-0243, I would push back on this. Namely, what makes the ExpressibleBy... protocols seem quite apt now is that the discussion has revealed that these protocols really carry meaning to users in a way that isn't just about compiler support for a syntax or about compile-time checks.
Consider: although some use cases would be nicer with let x: Int = 'a', we found that making an integer expressible by a character is just plain weird in many other circumstances. The idea of dividing by 'a', or asking if something is a multiple of 'a', was strongly opposed. I think this shows that conformance to an ExpressibleBy... protocol has to do with more than just telling the compiler to support a surface syntax and goes to what an instance of a type means and whether that meaning is congruent with what a particular literal syntax would suggest. (Contrast this with @dynamicCallable.)
Now, whether this tuple literal feature is appropriately considered an ExpressibleBy... type of thing, or whether it's more something in the same vein as @dynamicCallable and might be just @tupleConvertible...
This is an old discussion, but wouldn't it be completely analogous actually? A specific tuple is not more general than a specific type, right? E.g. (Int, String) vs Int.