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 <g...@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...@gmail.com> wrote: > > On Fri, Jun 9, 2017 at 07:33 Gor Gyolchanyan <g...@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...@gmail.com> wrote: >> >> >> On Fri, Jun 9, 2017 at 07:12 Gor Gyolchanyan <g...@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...@gmail.com> wrote: >>> >>> >>> On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <g...@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...@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@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 <gpar...@apple.com> wrote: >>>>> >>>>> >>>>> On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution < >>>>> swift-evolution@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 gpar...@apple.com Runtime Wrangler >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> swift-evolution mailing list >>>>> swift-evolution@swift.org >>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>> >>>> >>> >> >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution