[swift-evolution] [Proposal] Uniform Initialization Syntax

Gor Gyolchanyan gor at gyolchanyan.com
Fri Jun 9 06:12:17 CDT 2017


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.

> On Jun 9, 2017, at 2:07 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> 
> 
> On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor at gyolchanyan.com <mailto:gor at 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.wu at gmail.com <mailto:xiaodi.wu at 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 at swift.org <mailto:swift-evolution at 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 <gparker at apple.com <mailto:gparker at apple.com>> wrote:
>>> 
>>> 
>>>> On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at 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     gparker at apple.com <mailto:gparker at apple.com>     Runtime Wrangler
>>> 
>>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170609/1f469dc5/attachment.html>


More information about the swift-evolution mailing list