Implementing ExpressibleByTupleLiteral: how hard?

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.

1 Like

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

5 Likes

Integer literals are arbitrary-precision as of Swift 5; we got that in before locking down the ABI.

Edit: beaten :)

7 Likes

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.

4 Likes

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

3 Likes

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.

Terms of Service

Privacy Policy

Cookie Policy