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. 🙂

> On Jun 9, 2017, at 3:24 PM, Xiaodi Wu <[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]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to