on Thu Jun 02 2016, John McCall <[email protected]> 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>. 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 > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution > -- -Dave _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
