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]> 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
> 

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to