[swift-evolution] Proposal: 'T(literal)' should construct T using the appropriate literal protocol if possible

Dave Abrahams dabrahams at apple.com
Sun Jun 5 19:18:39 CDT 2016

on Thu Jun 02 2016, John McCall <swift-evolution at swift.org> wrote:

> The official way to build a literal of a specific type is to write the
> literal in an explicitly-typed context, like so:
>     let x: UInt16 = 7
> or
>     let x = 7 as UInt16
> Nonetheless, programmers often try the following:
>     UInt16(7)
> Unfortunately, this does not attempt to construct the value using the
> appropriate literal protocol; it instead performs overload resolution
> using the standard rules, i.e. considering only single-argument
> unlabelled initializers of a type which conforms to
> IntegerLiteralConvertible.  Often this leads to static ambiguities or,
> worse, causes the literal to be built using a default type (such as
> Int); this may have semantically very different results which are only
> caught at runtime.
> In my opinion, using this initializer-call syntax to build an
> explicitly-typed literal is an obvious and natural choice with several
> advantages over the "as" syntax.  However, even if you disagree, it's
> clear that programmers are going to continue to independently try to
> use it, so it's really unfortunate for it to be subtly wrong.
> Therefore, I propose that we adopt the following typing rule:
>   Given a function call expression of the form A(B) (that is, an
> expr-call with a single, unlabelled argument) where B is an
> expr-literal or expr-collection, if A has type T.Type for some type T
> and there is a declared conformance of T to an appropriate literal
> protocol for B, then the expression is always resolves as a literal
> construction of type T (as if the expression were written "B as A")
> rather than as a general initializer call.
> Formally, this would be a special form of the argument conversion
> constraint, since the type of the expression A may not be immediately
> known.

I realize this is somewhat tangential, but... IMO this may not be entirely
about literals.

We have a standard that full-width type conversions are written as a
label-free initializer
I believe that is partly responsible for setting up the expectation that
Int(42) works as one would expect.  It gets ultra-weird when you can
convert from type A to type B using B(someA) but you can't write
B(someB).  We should automatically generate a label-free “copy
initializer” for value types, to complete implementation of the expected
mental model.

> Note that, as specified, it is possible to suppress this typing rule
> by wrapping the literal in parentheses.  This might seem distasteful;
> it would be easy enough to allow the form of B to include extra
> parentheses.  It's potentially useful to have a way to suppress this
> rule and get a normal construction, but there are several other ways
> of getting that effect, such as explicitly typing the literal argument
> (e.g. writing "A(Int(B))").
> A conditional conformance counts as a declared conformance even if the
> generic arguments are known to not satisfy the conditional
> conformance.  This permits the applicability of the rule to be decided
> without having to first decide the type arguments, which greatly
> simplifies the type-checking problem (and may be necessary for
> soundness; I didn't explore this in depth, but it certainly feels like
> a very nasty sort of dependence).  We could potentially weaken this
> for cases where A is a direct type reference with bound parameters,
> e.g. Foo<Int>([]) or the same with a typealias, but I think there's
> some benefit from having a simpler specification, both for the
> implementation and for the explicability of the model.
> John.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


More information about the swift-evolution mailing list