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

Xiaodi Wu xiaodi.wu at gmail.com
Fri Jun 3 15:08:23 CDT 2016


On Fri, Jun 3, 2016 at 2:41 PM, Matthew Johnson <matthew at anandabits.com>
wrote:

>
> On Jun 3, 2016, at 1:36 PM, John McCall <rjmccall at apple.com> wrote:
>
> On Jun 2, 2016, at 6:48 PM, Matthew Johnson <matthew at anandabits.com>
> wrote:
> On Jun 2, 2016, at 6:51 PM, John McCall via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> On Jun 2, 2016, at 4:33 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> The IntegerLiteral type idea might be worth exploring. It does seem to
> provide some additional consistency. For example, wasn't it clarified on
> this list just recently that literals don't have a type and adopt one based
> on context? It'd be nice to point out that 42 is an IntegerLiteral when
> explaining that it's not an Int.
>
>
> I think that's a very promising way of thinking about literals.  Writing a
> literal creates a notional value whose type is the informal,
> infinite-precise type of all integer/FP/collection/etc. literals, which (1)
> can be implicitly converted to any type that implements the appropriate
> protocol and (2) in fact *must* be converted to some such type (possibly
> the default type for that literal) in order for the code to be executable.
> You can then think about this proposal as saying that an explicit
> conversion from that informal type to a literal-convertible type follows
> the same path as an implicit conversion would have.
>
>
> It sounds like the reason you don't want to remove the label is because it
> distinguishes the literal initializer and you don't want it called with a
> variable accidentally right?
>
> What if we do something like this:
>
> init(_ value: IntegerLiteralType, literal: IntegerLiteral = #literal)
>
> The idea here is that the IntegerLiteral type and friends are special
> types that have no members and cannot be used except by the literal
> convertible protocols and their conformances.  There is nothing you can do
> with the value at all.
>
> The trick is that by defaulting it to #literal we are indicating the
> compiler synthesizes the logical value, as with #file, #line, etc.  The
> compiler would refuse to synthesize this value when a literal is not used.
> This means it is never selected when the user provides a variable to a
> converting initializer.  If an independent initializer accepting a value of
> the same type also exists that would be selected.  However, when a literal
> *is* used and the type conforms to the relevant literal convertible
> protocol the compiler always synthesized the value making it always the
> most specific overload.
>
> Of course no runtime value would actually exist.  This is just a logical
> value marking the fact that a literal was used to call the initializer.
>
> This approach solves the same problem while retaining semantic consistency
> with the language (no label elision or short circuit of overload
> resolution).  The magic is arguably a lot more restrained - types for which
> values can only be supplied by the compiler.  We could artificially
> restrict usage of these types if we wanted to, but we wouldn't have to.
> Nothing could be accomplished by using the types anywhere else so nobody do
> so and it wouldn't be actively harmful to allow them to be used anywhere
> other types can be used.  Only the ability to create values of the type
> needs to be restricted and we can already write types like that by marking
> the initializers private.
>
> Any thoughts on this approach?
>
>
> This is still a special-case type-checking rule, which means that it's
> still basically my proposal except, instead of just rewriting the call to
> use a labeled literal initializer, it rewrites the call to use a magic
> initializer which cannot be used by users because it requires arguments
> that users cannot create.  I just don't understand the motivation here.
> It's not a simpler language model to use, explain, or implement; it
> purports to be conceptually simpler and less magical while actually
> inventing two magic new language concepts (IntegerLiteral and #literal) on
> top of the same special cases.
>
>
> I understand that this is still a special case and also that the
> implementation is pretty much identical.  But I think the way it is
> presented to users is important.
>
>  The motivation is to come up with a design that lets us implement your
> proposal without magically eliding a parameter label.  There is no other
> case in Swift where you can directly call any function (initializer or
> otherwise) without including labels for all parameters that have external
> labels.  Creating a special case to allow that here is inconsistent.  Why
> wouldn’t look for a way to do this that treats the signature consistently
> with the rest of the language and is more in line with how we handle other
> compiler magic (i.e. #file, #line, etc)?
>
> I am not the only one who feels that way.  Quoting Brent:
>
> "But if you're going to call `init(integerLiteral:)` like it's `init(_:)`,
> I don't think that's a good idea. Parameter labels are supposed to be
> significant; we don't want to lose that.”
>
> I agree with this.  That is the motivation for my suggestion.  I think
> it’s at least worth discussing as an alternative to magically allowing an
> external parameter label to be omitted.  Brent, what do you think of my
> suggestion?
>
>
> Literals are always going to involve some magic behind the scenes.  The
> non-trivial literals, for example, all actually traffic through some sort
> of builtin protocol (e.g. BuiltinIntegerLiteralConvertible) which cannot be
> implemented by users and whose construction necessarily involves a private
> API contract between the standard library and compiler that we reserve the
> right to change at will without bringing in swift-evolution.  All of the
> supposedly simple explanations eventually reach, "okay, but if Int isn't a
> special type, how does the literal turn into an Int in the first place",
> and that is just unanswerable, because it turns out that standard library
> types *are* actually special in the sense that they get to conform to
> BuiltinIntegerLiteralConvertible and nobody else does.
>
>
> Understood.  But Swift does a good job of making the magic feel consistent
> with the rest of the language.  That’s a good thing.  Allowing omission of
> a parameter label is a break from that IMO.
>
> I believe this is an important problem to solve but I also think we should
> try to find a way to solve it that doesn’t violate rules that apply
> everywhere else in the language (specifically regarding parameter labels).
>
>
Would it help if we had an attribute or contextual keyword to describe this
magic? By this I mean, what if we had `init(_: @literal Int)` or something
similar? The magic would be isolated to the fact that you'd have to supply
an integer literal to such an argument, which through behind-the-scenes
stuff becomes an Int.


> -Matthew
>
>
> John.
>
>
>
> John.
>
>
> On Thu, Jun 2, 2016 at 18:22 Brent Royal-Gordon via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> > So, you think that this syntax is enticing to new developers who
>> naturally think that the feature works the way that I'm proposing it should
>> work, and you think that the right solution is to make the syntax illegal
>> so that you can more conveniently tell them it doesn't work that way? :)
>>
>> I think the difference between a cast (which merely reinterprets a value
>> as a compatible type) and a fullwidth conversion (which creates a similar
>> instance of an incompatible type) is very important to understanding how to
>> write Swift, and we shouldn't muddy the waters by creating a magic syntax.
>>
>> > You can still tell them that it's a struct and you're calling an
>> initializer on it; it's just that the initializer chosen is the special
>> literal initializer because the argument is a literal.
>>
>> If you're planning to change `IntegerLiteralConvertible` and friends to
>> require a fullwidth conversion initializer like `init(_ value:
>> IntegerLiteralType)`, then this is simply an overload resolution rule. In
>> that case, I think your proposal is fine.
>>
>> But if you're going to call `init(integerLiteral:)` like it's `init(_:)`,
>> I don't think that's a good idea. Parameter labels are supposed to be
>> significant; we don't want to lose that.
>>
>> --
>> Brent Royal-Gordon
>> Architechies
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>
> _______________________________________________
> 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/20160603/4422cdc9/attachment.html>


More information about the swift-evolution mailing list