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/proposals/NNNN-factory-initializers.md > On Jun 10, 2017, at 12:43 PM, Gor Gyolchanyan <[email protected]> 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 <[email protected] >> <mailto:[email protected]>> 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/58b5a93b322aae998eb40574dee15fe54323de2e >> >> <https://github.com/apple/swift-evolution/pull/247/commits/58b5a93b322aae998eb40574dee15fe54323de2e> >> 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 >>> <[email protected] <mailto:[email protected]>> 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 >>>> <[email protected] <mailto:[email protected]>> wrote: >>>> >>>> On Fri, Jun 9, 2017 at 5:32 PM, Gor Gyolchanyan <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> Forked swift-evolution, created a draft proposal: >>>> >>>> https://github.com/technogen-gg/swift-evolution/blob/master/proposals/NNNN-uniform-initialization.md >>>> >>>> <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 <[email protected] >>>>> <mailto:[email protected]>> 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 <[email protected] >>>>> <mailto:[email protected]>> 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 <[email protected] >>>>>> <mailto:[email protected]>> wrote: >>>>>> >>>>>> On Fri, Jun 9, 2017 at 07:33 Gor Gyolchanyan <[email protected] >>>>>> <mailto:[email protected]>> 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 <[email protected] >>>>>>> <mailto:[email protected]>> wrote: >>>>>>> >>>>>>> >>>>>>> On Fri, Jun 9, 2017 at 07:12 Gor Gyolchanyan <[email protected] >>>>>>> <mailto:[email protected]>> 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 <[email protected] >>>>>>>> <mailto:[email protected]>> wrote: >>>>>>>> >>>>>>>> >>>>>>>> On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <[email protected] >>>>>>>> <mailto:[email protected]>> 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 <[email protected] >>>>>>>>> <mailto:[email protected]>> 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 >>>>>>>>> <[email protected] <mailto:[email protected]>> 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 <[email protected] >>>>>>>>>> <mailto:[email protected]>> wrote: >>>>>>>>>> >>>>>>>>>> >>>>>>>>>>> On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution >>>>>>>>>>> <[email protected] <mailto:[email protected]>> >>>>>>>>>>> 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 [email protected] <mailto:[email protected]> >>>>>>>>>> Runtime Wrangler >>>>>>>>>> >>>>>>>>>> >>>>>>>>> >>>>>>>>> _______________________________________________ >>>>>>>>> swift-evolution mailing list >>>>>>>>> [email protected] <mailto:[email protected]> >>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>>> >>>>> >>>> >>>> >>> >>> _______________________________________________ >>> swift-evolution mailing list >>> [email protected] <mailto:[email protected]> >>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >> >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
