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