[swift-evolution] Proposal: 'T(literal)' should construct T using the appropriate literal protocol if possible
Dave Abrahams
dabrahams at apple.com
Wed Jun 8 13:15:34 CDT 2016
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.
--
Dave
More information about the swift-evolution
mailing list