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

John McCall rjmccall at apple.com
Wed Jun 8 13:20:43 CDT 2016


> On Jun 8, 2016, at 11:15 AM, Dave Abrahams <dabrahams at apple.com> wrote:
> on Wed Jun 08 2016, John McCall <rjmccall-AT-apple.com> wrote:
> 
>>> On Jun 7, 2016, at 4:25 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>>> on Tue Jun 07 2016, John McCall <rjmccall-AT-apple.com> wrote:
>>> 
>>>>> On Jun 5, 2016, at 5:18 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
>>>>> on Thu Jun 02 2016, John McCall <swift-evolution at swift.org <mailto: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
>>>>> <https://swift.org/documentation/api-design-guidelines/#type-conversion
>>>>> <https://swift.org/documentation/api-design-guidelines/#type-conversion>>.
>>>>> 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.
>>>> 
>>>> That may also be a good idea, but it won't magically be preferred for
>>>> literal construction if the type has any other constructors of
>>>> literal-convertible type.
>>> 
>>> I know.  I'm saying, fixing this for literals without giving value types
>>> copy initializers leaves us with only a partial realization of a larger
>>> mental model to which I believe people are programming.
>> 
>> That's fair.  Would you like me to incorporate that into my proposal,
>> then?  I see the relation, but it's a pretty significant jump in
>> scope.
> 
> I'm not sure it should be in the same proposal; I just want it to be a
> part of the discussion.  We should be able to make incremental progress,
> but let's make sure we understand how our changes fit into the big picture.

Sure thing.  I agree that that is an interesting way of conceptualizing how
this proposal works, at least for value types where the initializer is synthesized:
you could think of it as saying that there's a rule to prefer the "copy initializer"
when the argument is a literal and the type is literally constructible.

The flip side is that that argument wouldn't apply to class types or (presumably)
generic type parameters, but we'd still want e.g. T(1) to work if T: IntegerLiteralConvertible.

John.


More information about the swift-evolution mailing list