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

Austin Zheng austinzheng at gmail.com
Thu Jun 2 15:44:30 CDT 2016

That makes sense. Thanks for sharing your reasoning.

On Thu, Jun 2, 2016 at 1:38 PM, John McCall <rjmccall at apple.com> wrote:

> On Jun 2, 2016, at 11:55 AM, Austin Zheng <austinzheng at gmail.com> wrote:
> I think we should actually go further.
> If this proposal is accepted, disallow "as" as a way of specifying the
> type to construct from a literal.
> I see no reason to restrict "as" like this.  We're not going to stop using
> type context as a way of determining the type of a literal, and "as" is a
> general feature for providing type context.
> If this proposal isn't accepted, disallow using literal values as the
> argument to one-unlabeled-argument constructors.
> I can't imagine accepting this, either; it would be a major regression in
> the usefulness of unlabeled initializers.  At best, this would be
> appropriate only when the type conforms to an appropriate literal protocol.
> In general, Swift gives unlabeled initializers an idiomatic meaning: they
> coerce the argument to the target type in a nominally value-preserving
> way.  One way of viewing this proposal is that it recognizes that there is
> an obvious way to do that when the target type supports being directly
> formed from the given kind of literal.  But if the target type doesn't
> support that, coercing a value of some other appropriate literal type is
> still a completely reasonable behavior.
> John.
> Austin
> On Thu, Jun 2, 2016 at 11:46 AM, Austin Zheng <austinzheng at gmail.com>
> wrote:
>> +1.
>> The primary advantage is that it aligns the language semantics with how
>> most programmers expect this common C-language-family idiom to behave and
>> removes a potential source of silently wrong code.
>> The primary disadvantage is that it introduces special-case behavior to
>> certain types of initializers (although, to be fair, this special-case
>> behavior is easily recognizable: unlabeled one-argument initializer with a
>> literal as the argument).
>> I think the advantage outweighs the disadvantage.
>> This problem should be addressed one way or another. I prefer this
>> solution, but if it is rejected for whatever reason we should at least
>> explicitly outlaw A(literal) syntax in favor of "literal as A".
>> Austin
>> On Thu, Jun 2, 2016 at 9:08 AM, John McCall via swift-evolution <
>> 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.
>>> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160602/12db7555/attachment.html>

More information about the swift-evolution mailing list