[swift-evolution] [Proposal] Uniform Initialization Syntax

Xiaodi Wu xiaodi.wu at gmail.com
Sun Jun 11 16:30:49 CDT 2017


On Sun, Jun 11, 2017 at 3:49 PM, Riley Testut <rileytestut at gmail.com> wrote:

> Some thoughts on updated proposal:
>
> • I strongly believe factory initializers should follow the progressive
> disclosure pattern, and importing all Objective-C initializers as factory
> initializers breaks this. While there is precedent for this because of the
> "open" access control, I'd prefer if maybe we compromised and any @objc
> initializer is assumed to have the same performance characteristics as a
> factory initializer, without having to prepend the factory keyword.
>

I'm not expert enough on Obj-C interop issues to make insightful comments
on this, but my naive impression here is that changing how _all_ Obj-C
initializers are imported by default seems...risky? Perhaps a summary of
how the compiler currently handles the issue is in order for this proposal,
so as to enable readers to evaluate this change.


> • While I did initially propose the factory keyword, I'm still not
> entirely sure "factory" is the right choice. Though if the consensus that
> is the right keyword, then I'll happily accept that.
> • Having "self" refer to the dynamic *type* of the returned instance
> seems...weird. While technically correct for a static method, I'd expect
> self to be an instance and Self to be the dynamic type just like any other
> initializer.
>

Agree: `self` referring to a type is unprecedented when it's not a static
method--this feels weird. If this is essential for the whole design, have
you considered maybe just calling it `static init`? (This raises a
potentially interesting thought about whether it'd make sense to also have
a `class init`...)

• Factory initializers should be used for *any *initializer that returns a
> value. Furthermore, I don't think we should limit factory initializers to
> just protocol extensions and classes. Sure it doesn't make sense for value
> types, but I don't think we should forbid that (and that way if you want to
> return a value instead of a assign to self, you'd just use factory
> intializer)
> • I don't think there should be *any* delegating to other factory
> initializers. Nothing would stop you from returning a value initialized via
> another factory method, however.
>

Hmm. That sounds workable and simpler, definitely.

Sorry if sounds a little rambled, don't have access to a computer for the
> rest of the day so typing this all up on my phone!
>
> On Jun 11, 2017, at 1:39 PM, Gor Gyolchanyan via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Here's the updated proposal:
>
> https://github.com/technogen-gg/swift-evolution/blob/
> master/proposals/NNNN-factory-initializers.md
>
> Is there anything else or are we good to go?
>
> On Jun 11, 2017, at 8:46 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> On Sun, Jun 11, 2017 at 12:43 PM, Gor Gyolchanyan <gor at gyolchanyan.com>
> wrote:
>
>> I have no better name besides a factory initializer for now.
>> I have thought about this some more and came to this conclusion about the
>> keyword:
>> Let the keyword be universally applied to all factory initializers, to
>> statically enforce the rules of factory initializers (see below), because
>> the more I think of it, the more I realize that you'd generally not want to
>> mix factory and non-factory initializers, due to their vastly differing
>> purposes.
>>
>> Having said that, my current understanding of this proposal is as follows:
>>
>> * Allow marking initializers inside protocol extensions, class
>> declarations and class extensions as `factory`.
>> * In initializers marked as `factory`:
>> * Change the implicit `self` parameter to mean `the dynamic type of the
>> enclosing type` (just like it does in static methods).
>> * Disallow delegating initialization to initializers not marked as
>> `factory`.
>> * Require terminating the initializer by either returning a compatible
>> type (a conforming type for protocols, a derived instance for classes) or
>> returning `nil` (if the initializer is failable).
>> * In initializers inside enum declarations, enum extensions, struct
>> declarations and struct extensions:
>> * Allow terminating the initializer by returning an instance of the type
>> being initialized.
>>
>
> Sounds reasonable to me.
>
> On Jun 11, 2017, at 7:38 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>
>> On Sun, Jun 11, 2017 at 10:34 AM, Gor Gyolchanyan <gor at gyolchanyan.com>
>> wrote:
>>
>>> I just didn't want to use the commonly proposed `factory` word, because
>>> it implies a specific semantic tied to the factory method pattern.
>>> I gave it another thought and I'm thinking maybe we can forego the
>>> annotation and have the compiler deduce it automatically.
>>> There are only two places where an indirect initializer can exist:
>>> * Protocol extensions, returning a conforming type.
>>> * Classes, returning an instance.
>>> It doesn't make sense to have this on value types, since they do not
>>> have subtypes of any kind.
>>> Indirect initializers are very unambiguous in protocol extensions,
>>> because the only other way of implementing an initializer in a protocol
>>> extension is via delegating initialization, so the indirect-ness of the
>>> initializer can be statically determined by whether or not there is a
>>> delegating initializer involved.
>>> If the initializer in a protocol extension has a delegating
>>> initialization on any execution path, then returning an instance is
>>> disallowed and vice versa. This will ensure strict separation of
>>> initializer types for the compiler to generate code for.
>>> If a failable initializer in a protocol extension unconditionally
>>> returns `nil`, then no initialization takes place anyway, so it doesn't
>>> matter, which one the compiler chooses.
>>> In classes this is a bit difficult, because besides delegating
>>> initializers, they also can initialize the members directly.
>>> So, in addition to the distinguishing rule for the protocol extensions,
>>> classes will also check whether any member is assigned to on any execution
>>> path.
>>>
>>> What do you think?
>>>
>>
>> Keywords aren't just for the compiler; they're for the human reader too!
>> If you believe the use of your proposed feature in protocol extensions is
>> unambiguous to humans as well as compilers, then IMO it makes sense not to
>> require another keyword in that place. I haven't thought deeply about
>> whether that would be the case.
>>
>> Clearly, you're saying that this is a more complicated situation with
>> classes; I think it makes sense for you to consider requiring a keyword
>> there. There is precedent for keywords modifying `init` to be required for
>> classes but not for value types (e.g., `convenience`).
>>
>> Regardless of whether a keyword is required or not, your feature needs a
>> name. And here again, I think it is puzzling that you are calling them
>> "indirect initializers" when there is already another meaning for
>> "indirect" in Swift. Distinct concepts should have distinct names.
>>
>> On Jun 11, 2017, at 5:53 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>
>>> On Sun, Jun 11, 2017 at 8:49 AM, Gor Gyolchanyan <gor at gyolchanyan.com>
>>> wrote:
>>>
>>>> Can you recall the reasons why the removal of access modifiers on
>>>> extensions was rejected?
>>>>
>>>
>>> It was an unassailable reason, really: people found this shorthand
>>> useful and wanted to continue to use it--it is the only way to specify that
>>> multiple members are public without explicitly labeling each one. The core
>>> team agreed it was useful.
>>>
>>> My takeaway from the whole episode (I was greatly in favor of removing
>>> this shorthand, as it's highly inconsistent with all other access modifier
>>> rules) is that in general, since the bar for new syntax is so high, if a
>>> shorthand made it into the language (and especially if it's kind of an
>>> inconsistent shorthand) the general presumption must be that it is highly
>>> desired.
>>>
>>> Also, do you think `indirect init` is confusing inside an `indirect
>>>> enum`?
>>>>
>>>
>>> I do. These are unrelated definitions of "indirect," and I'm puzzled why
>>> you'd actively choose to run into issues with the same word meaning two
>>> things when you could choose another word.
>>>
>>> On Jun 11, 2017, at 4:40 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>
>>>> Removal of access modifiers on extensions has been proposed, reviewed,
>>>> and rejected, so that’s that.
>>>>
>>>> In general, Swift uses distinct keywords for distinct concepts, unlike
>>>> Rust which likes to reuse keywords in clever ways; if you’re finding that
>>>> things are getting confusing with one word meaning two things, that
>>>> shouldn’t be an invitation to rip out existing syntax but is probably a
>>>> good sign you shouldn’t be repurposing that keyword.
>>>>
>>>>
>>>> On Sun, Jun 11, 2017 at 03:28 Adrian Zubarev via swift-evolution <
>>>> swift-evolution at swift.org> wrote:
>>>>
>>>>> Yeah, well I messed up my proposal from last year about removing the
>>>>> access modifier on extensions and wish now I wasn’t that confused back than
>>>>> and made it right.
>>>>>
>>>>> The indirect keyword is literally the same story. The docs only says
>>>>> that this is only a shortcut.
>>>>>
>>>>> „To enable indirection for all the cases of an enumeration, mark the
>>>>> entire enumeration with the indirect modifier—this is convenient when the
>>>>> enumeration contains many cases that would each need to be marked with the
>>>>> indirect modifier.“
>>>>>
>>>>> If you really wish to reuse that keyword here we might need to remove
>>>>> such shortcuts from the language (indirect enum, access modifier on
>>>>> extensions, anything else?).
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Adrian Zubarev
>>>>> Sent with Airmail
>>>>>
>>>>> Am 11. Juni 2017 um 10:12:38, Gor Gyolchanyan (gor at gyolchanyan.com)
>>>>> schrieb:
>>>>>
>>>>> I always wondered, why is `indirect` allowed on the `enum` itself?
>>>>> Wouldn't it make more sense to apply it to individual cases that
>>>>> recursively refer to the `enum`?
>>>>> This question also applies to access modifiers on extensions. So, what
>>>>> is it supposed to do? Change the default access modifier from `internal` to
>>>>> whatever I specify? That's just confusing, reduces readability and the
>>>>> syntactic gain is marginal at best.
>>>>> If the `indirect` confusion becomes real, I'd suggest getting rid of
>>>>> `indirect enum` and using `indirect case` instead.
>>>>>
>>>>> On Jun 11, 2017, at 11:05 AM, Adrian Zubarev via swift-evolution <
>>>>> swift-evolution at swift.org> wrote:
>>>>>
>>>>> The proposal is looking good to me. :) It will also enable easy
>>>>> support for custom views using XIBs in iOS development without unnecessary
>>>>> view nesting.
>>>>>
>>>>> For instance the function from this example https://stackoverflow.
>>>>> com/a/43123783/4572536 could be used directly inside an init:
>>>>>
>>>>> class MyView : UIView {
>>>>>
>>>>>       indirect init() {
>>>>>             return MyView.instantiateFromXib()
>>>>>             // Or after SR-0068
>>>>>             return Self.instantiateFromXib()
>>>>>       }
>>>>> }
>>>>>
>>>>> There is still one little thing that bothers me, it might be a little
>>>>> bit confusing to have two different meanings of indirect on enums.
>>>>>
>>>>> indirect enum ArithmeticExpression {
>>>>>     case number(Int)
>>>>>     case addition(ArithmeticExpression, ArithmeticExpression)
>>>>>     case multiplication(ArithmeticExpression, ArithmeticExpression)
>>>>>
>>>>>     // This might makes no sense, but it would still be possible after
>>>>>     // this proposal.
>>>>>     indirect init(other: ArithmeticExpression) {
>>>>>        return other
>>>>>     }
>>>>>
>>>>>     // Furthermore if the keyboard is applied to the enum
>>>>>     // directly all other `indirect` uses are inferred.
>>>>>     // Will this be implicitly `indirect` because of the previous fact?
>>>>>     init() { … }
>>>>> }
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Adrian Zubarev
>>>>> Sent with Airmail
>>>>>
>>>>> Am 11. Juni 2017 um 00:38:56, Riley Testut via swift-evolution (
>>>>> swift-evolution at swift.org) schrieb:
>>>>>
>>>>> Awesome! Updated my proposal to include what I believed to be the
>>>>> relevant portions of your indirect initializer idea. Let me know if there’s
>>>>> anything I missed or should change :-)
>>>>>
>>>>> https://github.com/rileytestut/swift-evolution/blob/master/p
>>>>> roposals/NNNN-factory-initializers.md
>>>>>
>>>>> On Jun 10, 2017, at 12:43 PM, Gor Gyolchanyan <gor at gyolchanyan.com>
>>>>> wrote:
>>>>>
>>>>> Hi, Riley!
>>>>>
>>>>> I think that's a great idea! We can merge the second part of my
>>>>> proposal (the `indirect init`) into your one and refine and consolidate the
>>>>> prerequisite proposal (about returning from `init` and possibly in-place
>>>>> member initializers) and bunch them up into a proposal cluster (the way
>>>>> swift coders did).
>>>>> Feel free to tear out any chunks from my proposal, while I think about
>>>>> a more in-depth rationale about revamping initialization syntax. 🙂
>>>>>
>>>>> On Jun 10, 2017, at 8:36 PM, Riley Testut <rileytestut at gmail.com>
>>>>> wrote:
>>>>>
>>>>> Hi Gor 👋
>>>>>
>>>>> I’m very much in fan of a unified initialization syntax. I submitted
>>>>> my own proposal for factory initializers a while back, but since it wasn’t
>>>>> a focus of Swift 3 or 4 I haven’t followed up on it recently. In the time
>>>>> since last working on it, I came to my own conclusion that rather than
>>>>> focusing on factory initialization, the overall initialization process
>>>>> should be simplified, which I’m glad to see someone else has realized as
>>>>> well :-)
>>>>>
>>>>> Here’s my proposal for reference: https://github.com/
>>>>> apple/swift-evolution/pull/247/commits/58b5a93b322aae998eb40
>>>>> 574dee15fe54323de2e Originally I used the “factory” keyword, but I
>>>>> think your “indirect” keyword may be a better fit (since it has precedent
>>>>> in the language and is not limited to “just” being about factory
>>>>> initialization). To divide your proposal up into smaller pieces for review,
>>>>> maybe we could update my proposal to use your indirect keyword, and then
>>>>> start a separate topic/proposal for the remaining aspects of your proposal?
>>>>> I agree that splitting it into smaller chunks may be better for the process.
>>>>>
>>>>> Let me know what you think!
>>>>>
>>>>> Riley
>>>>>
>>>>>
>>>>> On Jun 10, 2017, at 3:33 AM, Gor Gyolchanyan via swift-evolution <
>>>>> swift-evolution at swift.org> wrote:
>>>>>
>>>>>
>>>>> This is a very interesting read.
>>>>>
>>>>> Thanks you! I tried to make it as clear and detailed as possible. 🙂
>>>>>
>>>>>
>>>>> We did not discuss the 'indirect' idea at all on this list. Did you
>>>>> come up with it just now? In any case, my suggestion as to moving forward
>>>>> would be this:
>>>>>
>>>>> I was writing the proposal and was just about to write `factory init`,
>>>>> when it occurred to me: enums already have a keyword that does something
>>>>> very similar. It seemed to me that an initializer that doesn't initialize
>>>>> the instance in-place, but returns a completely separate instance from
>>>>> somewhere else, is kinda "indirectly" initializing the instance. Plus, the
>>>>> already established keyword and its semantic would reduce the learning
>>>>> curve for this new feature and separate it from a single specific use case
>>>>> (the "factory method" pattern).
>>>>>
>>>>>
>>>>> - Do you feel that both halves of your draft (expanding `return` in
>>>>> initializers, and `indirect` initializers) should absolutely be one
>>>>> proposal, or can they be separated?
>>>>>
>>>>> I think the `return` can be easily implemented first, while opening up
>>>>> an opportunity to later implement `indirect init`. The reason why I unified
>>>>> them was that the `return` idea on its own has very limited merit and could
>>>>> the thought of as a low-priority cosmetic enhancement. I wouldn't want it
>>>>> to be viewed that way because the primary purpose of that idea is to enable
>>>>> `indirect init` (which Cocoa and Cocoa Touch developers would be very happy
>>>>> about).
>>>>>
>>>>>
>>>>> a) If they can be separated because each half has individual merit,
>>>>> then these ideas may be more likely to succeed as separate proposals, as
>>>>> each can be critiqued fully and judged independently as digestible units.
>>>>>
>>>>>
>>>>> Very good point. The challenge is to correctly separate them, without
>>>>> losing context in their respective proposals and without bleeding the
>>>>> proposals into each other.
>>>>>
>>>>>
>>>>>
>>>>> b) If you intend to tackle all your ideas all at once, that's going to
>>>>> be a much bigger change--in terms of review effort, likely bikeshedding,
>>>>> and implementation effort. It'll probably be best to solicit initial
>>>>> feedback on this list first about `indirect` initializers, even if just to
>>>>> familiarize the community with the idea, before launching into a pitch of
>>>>> the whole proposal.
>>>>>
>>>>> I'd never send a pull request to swift-evolution without thoroughly
>>>>> discussing it here. I just though, if I'm going to write a whole proposal
>>>>> with examples and motivation, it would be easier to demonstrate it and
>>>>> discuss in with the community If I just went ahead and wrote the whole
>>>>> thing and sent the link. This way it would be clearer to the reader and the
>>>>> discussed changes would be accurately reflected by the commits I'd make to
>>>>> my proposal.
>>>>>
>>>>> Original Message
>>>>>
>>>>> On Jun 10, 2017, at 2:38 AM, Daryle Walker via swift-evolution <
>>>>> swift-evolution at swift.org> wrote:
>>>>>
>>>>> On Fri, Jun 9, 2017 at 5:32 PM, Gor Gyolchanyan <gor at gyolchanyan.com>
>>>>> wrote:
>>>>>
>>>>>> Forked swift-evolution, created a draft proposal:
>>>>>>
>>>>>> https://github.com/technogen-gg/swift-evolution/blob/master/
>>>>>> proposals/NNNN-uniform-initialization.md
>>>>>>
>>>>>> This is my first proposal, so I might have missed something or
>>>>>> composed it wrong, so please feel free to comment, fork and send pull
>>>>>> requests. 🙂
>>>>>>
>>>>>>
>>>>> This is a very interesting read. We did not discuss the 'indirect'
>>>>> idea at all on this list. Did you come up with it just now? In any case, my
>>>>> suggestion as to moving forward would be this:
>>>>>
>>>>> - Do you feel that both halves of your draft (expanding `return` in
>>>>> initializers, and `indirect` initializers) should absolutely be one
>>>>> proposal, or can they be separated?
>>>>>
>>>>> a) If they can be separated because each half has individual merit,
>>>>> then these ideas may be more likely to succeed as separate proposals, as
>>>>> each can be critiqued fully and judged independently as digestible units.
>>>>>
>>>>> b) If you intend to tackle all your ideas all at once, that's going to
>>>>> be a much bigger change--in terms of review effort, likely bikeshedding,
>>>>> and implementation effort. It'll probably be best to solicit initial
>>>>> feedback on this list first about `indirect` initializers, even if just to
>>>>> familiarize the community with the idea, before launching into a pitch of
>>>>> the whole proposal.
>>>>>
>>>>>
>>>>> On Jun 9, 2017, at 3:24 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>>>
>>>>>> Cool. I have reservations about idea #3, but we can tackle that
>>>>>> another day. (Real life things beckon.) But suffice it to say that I now
>>>>>> really, really like your idea #2.
>>>>>> On Fri, Jun 9, 2017 at 08:06 Gor Gyolchanyan <gor at gyolchanyan.com>
>>>>>> wrote:
>>>>>>
>>>>>>> You know, come to think of it, I totally agree, and here's why:
>>>>>>> A normal initializer (if #2 is accepted) would *conceptually* have
>>>>>>> the signature:
>>>>>>>
>>>>>>> mutating func `init`(...) -> Self
>>>>>>>
>>>>>>> Which would mean that both `self` and the returned result are
>>>>>>> non-optional.
>>>>>>> A failable initializer could then have the signature:
>>>>>>>
>>>>>>> mutating func `init`() -> Self?
>>>>>>>
>>>>>>> Which would make the returned result optional, but leave `self`
>>>>>>> non-optional.
>>>>>>> This would make `return nil` less out-of-place, like you said, while
>>>>>>> still leaving `self` as a set-exactly-once `inout Self`.
>>>>>>> A factory initializer would then have the signature:
>>>>>>>
>>>>>>> static func `init`(...) -> Self
>>>>>>>
>>>>>>> or in case of a failable factory initializer:
>>>>>>>
>>>>>>> static func `init`(...) -> Self?
>>>>>>>
>>>>>>> Which would still make sense with the now legal `return ...` syntax,
>>>>>>> while adding the restriction of not having any `self` at all.
>>>>>>> So, annotating the initializer with the keyword `factory` would
>>>>>>> cause it to change the signature as well as remove any compiler assumptions
>>>>>>> about the dynamic type of the returned instance.
>>>>>>>
>>>>>>> In addition, idea #3 would be available for more deterministic
>>>>>>> in-place initialization.
>>>>>>>
>>>>>>> On Jun 9, 2017, at 2:47 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>>>>
>>>>>>> On Fri, Jun 9, 2017 at 07:33 Gor Gyolchanyan <gor at gyolchanyan.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> So far, we've discussed two ways of interpreting `self = nil`, both
>>>>>>>> of which have a sensible solution, in my opinion:
>>>>>>>>
>>>>>>>> 1. It's a special rule like you said, which can be seen as
>>>>>>>> counter-intuitive, but recall that `return nil` is just as much of a
>>>>>>>> special rule and is also largely counter-intuitive.
>>>>>>>>
>>>>>>>
>>>>>>> `return nil` is “special,” but it doesn’t conflict with any other
>>>>>>> syntax because the initializer notionally has no return value. Personally,
>>>>>>> I have always disliked `return nil` in failable initializers for that
>>>>>>> reason, but I couldn’t come up with a better alternative.
>>>>>>>
>>>>>>> Your proposed idea to allow returning any value is interesting
>>>>>>> because, in the case of a failable initializer, `return nil` continues to
>>>>>>> have the same meaning if we consider the return value of the initializer to
>>>>>>> be of type `Self?`. For that reason, I think your idea #2 is quite clever,
>>>>>>> and it would go a long way in making `return nil` a lot less odd. It also
>>>>>>> increases the expressivity of initializers because it allows one to set the
>>>>>>> value of self and also return in one statement, clearly demonstrating the
>>>>>>> intention that no other code in the initializer should be run before
>>>>>>> returning.
>>>>>>>
>>>>>>> For all of those reasons, I think idea #2 is a winning idea.
>>>>>>>
>>>>>>> The benefit of `self = nil` is that it's much more in line with
>>>>>>>> initialization semantics, it provides more uniform syntax and it's a bit
>>>>>>>> less restrictive.
>>>>>>>>
>>>>>>>> 2. It's an `inout Self!`, like Greg said, which can be seen as more
>>>>>>>> cumbersome. Implicitly unwrapped optionals are a bit difficult, but this
>>>>>>>> "variation" of it is much more restrictive then the normal ones, because
>>>>>>>> unlike normal implicitly unwrapped optionals, this one cannot be accessed
>>>>>>>> after being assigned nil (and it also cannot be indirectly assigned `nil`,
>>>>>>>> because escaping `self` is not allowed before full initialization), so
>>>>>>>> there is only one possible place it can be set to nil and that's directly
>>>>>>>> in the initializer. This means that `self` can be safely treated as `inout
>>>>>>>> Self` before being set to nil (and after being set to nil, it doesn't
>>>>>>>> matter any more because you aren't allowed to access it, due to not being
>>>>>>>> fully initialized).
>>>>>>>>
>>>>>>>
>>>>>>> I have to say, I don’t like either of these explanations at all. I
>>>>>>> think having a “special” IUO is a difficult sell; it is just conceptually
>>>>>>> too complicated, and I don’t agree that it gains you much.
>>>>>>>
>>>>>>> By your own admission, `self = nil` is wonky, and making the
>>>>>>> language wonkier because it currently has a parallel wonky feature in
>>>>>>> `return nil` seems like the wrong way to go. In addition, there’s nothing
>>>>>>> gained here that cannot be done with a defer statement; of course, defer
>>>>>>> statements might not be very elegant, but it would certainly be less wonky
>>>>>>> than inventing a new variation on an IUO to allow assignment of nil to
>>>>>>> self... For those reasons, I conclude that I’m not excited about your idea
>>>>>>> #1.
>>>>>>>
>>>>>>> Overall, I'd go with #2 because it involves much less confusing
>>>>>>>> magic and the restrictions of `self as inout Self!` are imposed by already
>>>>>>>> existing and well-understood initialization logic, so the provided
>>>>>>>> guarantees don't really come at the cost of much clarity.
>>>>>>>>
>>>>>>>> On Jun 9, 2017, at 2:23 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> On Fri, Jun 9, 2017 at 07:12 Gor Gyolchanyan <gor at gyolchanyan.com>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> I think a good approach would be to have `self = nil` only mean
>>>>>>>>> `the initializer is going to fail` because if your type is
>>>>>>>>> ExpressibleByNilLiteral, it means that the `nil` of your type already
>>>>>>>>> carries the same meaning as if your type was not ExpressibleByNilLiteral
>>>>>>>>> and was an optional instead, so having a failable initializer doesn't
>>>>>>>>> really make sense in that case (since you could've initialized `self` to
>>>>>>>>> its own `nil` in case of failure). Still, some valid use cases may exist,
>>>>>>>>> so the natural (and quite intuitive) way to circumvent this would be to
>>>>>>>>> call `self.init(nilLiteral: ())` directly.
>>>>>>>>>
>>>>>>>>
>>>>>>>> So you would create a special rule that `self = nil` means a
>>>>>>>> different thing in an initializer than it does in a function? Essentially,
>>>>>>>> then, you’re creating your own variation on an implicitly unwrapped
>>>>>>>> optional, where `self` is of type `inout Self?` for assignment in
>>>>>>>> initializers only but not for any other purpose. Implicitly unwrapped
>>>>>>>> optionals are hard to reason about, and having a variation on it would be
>>>>>>>> even harder to understand. I don’t think this is a workable design.
>>>>>>>>
>>>>>>>> It might be possible to have `self` be of type `inout Self?`;
>>>>>>>> however, I do think Greg is right that it would create more boilerplate
>>>>>>>> than the current situation.
>>>>>>>>
>>>>>>>> On Jun 9, 2017, at 2:07 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor at gyolchanyan.com>
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>> The type of `self` could remain `inout Self` inside the failable
>>>>>>>>>> initializer. The ability to assign nil would be a compiler magic (much like
>>>>>>>>>> `return nil` is compiler magic) that is meant to introduce uniformity to
>>>>>>>>>> the initialization logic.
>>>>>>>>>>
>>>>>>>>>> The idea is to define all different ways initialization can take
>>>>>>>>>> place and expand them to be used uniformly on both `self` and all its
>>>>>>>>>> members, as well as remove the ways that do not make sense for their
>>>>>>>>>> purpose.
>>>>>>>>>>
>>>>>>>>>> Currently, there are 3 ways of initializing self as a whole:
>>>>>>>>>> 1. delegating initializer
>>>>>>>>>> 2. assigning to self
>>>>>>>>>> 3. returning nil
>>>>>>>>>>
>>>>>>>>>> #1: The delegating initializer is pretty much perfect at this
>>>>>>>>>> point, in my opinion, so no changes there.
>>>>>>>>>>
>>>>>>>>>> #2: The only exception in assigning to self is the `nil` inside
>>>>>>>>>> failable initializers.
>>>>>>>>>>
>>>>>>>>>> #3:  The only thing that can be returned from an initializer is
>>>>>>>>>> `nil`, which is compiler magic, so we can thing of it as a misnomer
>>>>>>>>>> (because we aren't really **returning** anything).
>>>>>>>>>>
>>>>>>>>>> If, for a second, we forget about potential factory initializers,
>>>>>>>>>> returning anything from an initializer doesn't make much sense, because an
>>>>>>>>>> initializer is conceptually meant to bring an existing object in memory to
>>>>>>>>>> a type-specific valid state. This semantic was very explicitly in
>>>>>>>>>> Objective-C with `[[MyType alloc] init]`. Especially since even
>>>>>>>>>> syntactically, the initializer does not specify any return type, the idea
>>>>>>>>>> of returning from an initializer is counter-intuitive both syntactically
>>>>>>>>>> and semantically.
>>>>>>>>>>
>>>>>>>>>> The actual *behavior* of `return nil` is very sensible, so the
>>>>>>>>>> behavior, I imagine `self = nil`, would largely mean the same (except not
>>>>>>>>>> needed to return immediately and allowing non-self-accessing code to be
>>>>>>>>>> executed before return). Being able to assign `nil` to a non-optional
>>>>>>>>>> (ExpressibleByNilLiteral doesn't count) may feel a bit wonky,
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> What happens when Self is ExpressibleByNilLiteral and you want to
>>>>>>>>> initialize self to nil? That is what `self = nil` means if `self` is of
>>>>>>>>> type `inout Self`. If `self` is of type `inout Self` and Self is not
>>>>>>>>> ExpressibleByNilLiteral, then it must be an error to assign nil to self.
>>>>>>>>> Anything else does not make sense, unless `self` is of type `inout Self?`.
>>>>>>>>>
>>>>>>>>> but not as wonky as returning nil from something that is meant to
>>>>>>>>>> initialize an object in-place and doesn't look like it should return
>>>>>>>>>> anything.
>>>>>>>>>>
>>>>>>>>>> # Factory Initializers
>>>>>>>>>>
>>>>>>>>>> In case of factory initializers, the much discussed `factory
>>>>>>>>>> init` syntax could completely flip this logic, but making the initializer
>>>>>>>>>> essentially a static function that returns an object. In this case the
>>>>>>>>>> initializer could be made to specify the return type (that is the supertype
>>>>>>>>>> of all possible factory-created objects) and assigning to self would be
>>>>>>>>>> forbidden because there is not self yet:
>>>>>>>>>>
>>>>>>>>>> extension MyProtocol {
>>>>>>>>>>
>>>>>>>>>> public factory init(weCool: Bool) -> MyProtocol {
>>>>>>>>>> self = MyImpl() // error: cannot assign to `self` in a factory
>>>>>>>>>> initializer
>>>>>>>>>> self.init(...) // error: cannot make a delegating initializer
>>>>>>>>>> call in a factory initializer
>>>>>>>>>> if weCool {
>>>>>>>>>> return MyCoolImpl()
>>>>>>>>>> } else {
>>>>>>>>>> return MyUncoolImpl()
>>>>>>>>>> }
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> # In-place Member Initializers
>>>>>>>>>>
>>>>>>>>>> In addition, member initialization currently is only possible
>>>>>>>>>> with #2 (as in `self.member = value`), which could be extended in a
>>>>>>>>>> non-factory initializer to be initializable in-place like this:
>>>>>>>>>>
>>>>>>>>>> self.member.init(...)
>>>>>>>>>>
>>>>>>>>>> This would compliment the delegating initialization syntax, while
>>>>>>>>>> giving a more reliable performance guarantee that this member will not be
>>>>>>>>>> copy-initialized.
>>>>>>>>>>
>>>>>>>>>> On Jun 9, 2017, at 1:32 PM, Xiaodi Wu <xiaodi.wu at gmail.com>
>>>>>>>>>> wrote:
>>>>>>>>>>
>>>>>>>>>> If `self` is not of type `inout Self?`, then what is the type of
>>>>>>>>>> `self` such that you may assign it a value of `nil`?
>>>>>>>>>>
>>>>>>>>>> It certainly cannot be of type `inout Self`, unless `Self`
>>>>>>>>>> conforms to `ExpressibleByNilLiteral`, in which case you are able to assign
>>>>>>>>>> `self = nil` an unlimited number of times–but that has a totally different
>>>>>>>>>> meaning.
>>>>>>>>>>
>>>>>>>>>> Could `self` be of type `inout Self!`? Now that implicitly
>>>>>>>>>> unwrapped optionals are no longer their own type, I’m not sure that’s
>>>>>>>>>> possible. But even if it were, that seems unintuitive and potentially
>>>>>>>>>> error-prone.
>>>>>>>>>>
>>>>>>>>>> So I think Greg is quite right that, to enable this feature,
>>>>>>>>>> `self` would have to be of type `inout Self?`–which is intriguing but
>>>>>>>>>> potentially more boilerplatey than the status quo.
>>>>>>>>>> On Fri, Jun 9, 2017 at 05:24 Gor Gyolchanyan via swift-evolution <
>>>>>>>>>> swift-evolution at swift.org> wrote:
>>>>>>>>>>
>>>>>>>>>>> Good point, but not necessarily.
>>>>>>>>>>> Since you cannot access `self` before it being fully initialized
>>>>>>>>>>> and since `self` can only be initialized once, this would mean that after
>>>>>>>>>>> `self = nil`, you won't be allowed to access `self` in your initializer at
>>>>>>>>>>> all.You'll be able to do any potential, cleanup though.
>>>>>>>>>>> Also, since there can be only one `self = nil`, there's no
>>>>>>>>>>> reason to treat `self` as `inout Self?`, because the only place it can be
>>>>>>>>>>> `nil` is the place it cannot be accessed any more.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Jun 9, 2017, at 7:45 AM, Greg Parker <gparker at apple.com>
>>>>>>>>>>> wrote:
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution <
>>>>>>>>>>> swift-evolution at swift.org> wrote:
>>>>>>>>>>>
>>>>>>>>>>> 1. Arbitrary `self` Assignments In Intializers
>>>>>>>>>>>
>>>>>>>>>>> The first ideas is to allow `self = nil` inside failable
>>>>>>>>>>> initializers (essentially making `self` look like `inout Self?` instead of
>>>>>>>>>>> `inout Self` with magical `return nil`), so that all initializers uniformly
>>>>>>>>>>> can be written in `self = ...` form for clarity and convenience purposes.
>>>>>>>>>>> This should, theoretically, be nothing but a `defer { return nil }` type of
>>>>>>>>>>> rewrite, so I don't see any major difficulties implementing this. This is
>>>>>>>>>>> especially useful for failable-initializing enums where the main switch
>>>>>>>>>>> simply assigns to self in all cases and the rest of the initializer does
>>>>>>>>>>> some post-processing.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I don't see how to avoid source incompatibility and uglification
>>>>>>>>>>> of failable initializer implementations here. Allowing `self = nil` inside
>>>>>>>>>>> a failable initializer would require `self` to be an optional. That in turn
>>>>>>>>>>> would require every use of `self` in the initializer to be nil-checked or
>>>>>>>>>>> forced. I don't think that loss everywhere outweighs the gain of `self =
>>>>>>>>>>> nil` in some places.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> Greg Parker     gparker at apple.com     Runtime Wrangler
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> _______________________________________________
>>>>>>>>>>> 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
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> 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
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> 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/20170611/17885938/attachment.html>


More information about the swift-evolution mailing list