[Proposal] Uniform Initialization Syntax


(Gor Gyolchanyan) #1

Disclaimer: I do realize that any of the following ideas may have been discussed before and/or there might be a good reason for their lack of implementation, so please go easy in me. :slightly_smiling_face:

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.

2. Arbitrary `return` Statements In Intializers

The second idea is to allow `return ...` inside all initializers, which should also, theoretically, be a simple rewrite to `self = ...; return`. This one is to complement the existing `return nil` and allow some super-short initializers with a switch that returns in all cases or a more complex initializer that has a lot of guard statements.

2.1. Future Factory Initializers

In addition, the `return ...` syntax has the benefit for potential factory initializers. So far, the proposals for factory initializers involved a keyword attached to the initializer, which just complicates the lexical structure of the language and adds unnecessary complication to the interface of types. Currently, factory initializers imported from Objective-C or from C using the `__attribute__((swift_name("MyType.init(self:...)")))` look like normal initializers (an in case of C, the return value doesn't even have to be related to the enclosing type in any way), but behave as you'd expect: you call the initializer and the result is a value that *should* be a subtype of the type you've called the initializer for. So, if in the future Swift gets native factory initializers (including, most importantly, in protocols), it won't require special syntax, because simply returning an instance of a subtype (with a compile-time check, of course) would look and behave very intuitively. This would also be very useful for singletons, which would use a private initializer for creating the instance and a public factory initializer for returning it.

3. Failable Member Initialization

The third idea is to allow writing `self.member = MemberType?(...)` or `self.member = .init?(...)` (the exact syntax is up to debate) inside failable initializers, which would be simply rewritten as:

guard let _self_member = MemberType(...) else {
聽聽return nil
}
self.member = _self_member

This will dramatically reduce the boilerplate and visual clutter form complex failable initializers that call other failable initializers. A good use case would be complex `LosslessStringConvertible` types with many `LosslessStringConvertible` members.

So, what do you guys think?


(David Sweeris) #2

#1 & #3 would violate Swift's rule about having to fully initialize all properties in inits.

My initial reaction is to like #2, though, assuming I understand it correctly.

- Dave Sweeris

路路路

On Jun 8, 2017, at 05:09, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org> wrote:

Disclaimer: I do realize that any of the following ideas may have been discussed before and/or there might be a good reason for their lack of implementation, so please go easy in me. :slightly_smiling_face:

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.

2. Arbitrary `return` Statements In Intializers

The second idea is to allow `return ...` inside all initializers, which should also, theoretically, be a simple rewrite to `self = ...; return`. This one is to complement the existing `return nil` and allow some super-short initializers with a switch that returns in all cases or a more complex initializer that has a lot of guard statements.

2.1. Future Factory Initializers

In addition, the `return ...` syntax has the benefit for potential factory initializers. So far, the proposals for factory initializers involved a keyword attached to the initializer, which just complicates the lexical structure of the language and adds unnecessary complication to the interface of types. Currently, factory initializers imported from Objective-C or from C using the `__attribute__((swift_name("MyType.init(self:...)")))` look like normal initializers (an in case of C, the return value doesn't even have to be related to the enclosing type in any way), but behave as you'd expect: you call the initializer and the result is a value that *should* be a subtype of the type you've called the initializer for. So, if in the future Swift gets native factory initializers (including, most importantly, in protocols), it won't require special syntax, because simply returning an instance of a subtype (with a compile-time check, of course) would look and behave very intuitively. This would also be very useful for singletons, which would use a private initializer for creating the instance and a public factory initializer for returning it.

3. Failable Member Initialization

The third idea is to allow writing `self.member = MemberType?(...)` or `self.member = .init?(...)` (the exact syntax is up to debate) inside failable initializers, which would be simply rewritten as:

guard let _self_member = MemberType(...) else {
聽聽聽return nil
}
self.member = _self_member

This will dramatically reduce the boilerplate and visual clutter form complex failable initializers that call other failable initializers. A good use case would be complex `LosslessStringConvertible` types with many `LosslessStringConvertible` members.

So, what do you guys think?


(Charles Srstka) #3

Assigning things to self can already be done with structs and enums:

struct S {
聽聽let foo: String
聽聽
聽聽init(foo: String) {
聽聽聽聽self.foo = foo
聽聽}
聽聽
聽聽init(bar: String) {
聽聽聽聽self = S(foo: bar) // works fine
聽聽}
}

enum E {
聽聽case foo
聽聽case bar
聽聽
聽聽init(isFoo: Bool) {
聽聽聽聽if isFoo {
聽聽聽聽聽聽self = .foo // works fine
聽聽聽聽} else {
聽聽聽聽聽聽self = .bar // ditto
聽聽聽聽}
聽聽}
}

The one restriction is that in the case of the struct, you can鈥檛 have initialized any of the immutable properties before assigning to self.

struct S {
聽聽let foo: String
聽聽
聽聽init(foo: String) {
聽聽聽聽self.foo = foo
聽聽}
聽聽
聽聽init(bar: String) {
聽聽聽聽self.foo = "Foo"
聽聽聽聽self = S(foo: bar) // error: immutable value 'self.foo' may only be initialized once
聽聽}
}

The only real thing being proposed here, as I see it, is to make this behavior conceptually consistent with classes. Anything that simplifies the rather Byzantine rules surrounding initializers, while simultaneously enabling factory initializers, is a win in my book.

Charles

路路路

On Jun 8, 2017, at 9:19 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

#1 & #3 would violate Swift's rule about having to fully initialize all properties in inits.

My initial reaction is to like #2, though, assuming I understand it correctly.


(Adrian Zubarev) #4

Isn鈥檛 1 und 2.1 almost the same stuff? I鈥檝e asked once if there is a chance for Swift to support init(_ other: Self) { self = other } for classes. Joe Groff replied this is called 鈥渇actory initializer鈥.

This feature is highly needed for all the iOS developers out there who abuse NIBs and create a custom UIView in a nib file then instead of assigning it to a UIViewController they nest it in another container view, which is not what it鈥檚 meant for in the first place. Factory initializer could solve this issue by simply assigning the instance created from a nib file to self. The nested view hierarchy would disappear and it won鈥檛 be that much of an abuse anymore.

路路路

--
Adrian Zubarev
Sent with Airmail

Am 8. Juni 2017 um 14:09:30, Gor Gyolchanyan via swift-evolution (swift-evolution@swift.org) schrieb:

Disclaimer: I do realize that any of the following ideas may have been discussed before and/or there might be a good reason for their lack of implementation, so please go easy in me. :slightly_smiling_face:

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.

2. Arbitrary `return` Statements In Intializers

The second idea is to allow `return ...` inside all initializers, which should also, theoretically, be a simple rewrite to `self = ...; return`. This one is to complement the existing `return nil` and allow some super-short initializers with a switch that returns in all cases or a more complex initializer that has a lot of guard statements.

2.1. Future Factory Initializers

In addition, the `return ...` syntax has the benefit for potential factory initializers. So far, the proposals for factory initializers involved a keyword attached to the initializer, which just complicates the lexical structure of the language and adds unnecessary complication to the interface of types. Currently, factory initializers imported from Objective-C or from C using the `__attribute__((swift_name("MyType.init(self:...)")))` look like normal initializers (an in case of C, the return value doesn't even have to be related to the enclosing type in any way), but behave as you'd expect: you call the initializer and the result is a value that *should* be a subtype of the type you've called the initializer for. So, if in the future Swift gets native factory initializers (including, most importantly, in protocols), it won't require special syntax, because simply returning an instance of a subtype (with a compile-time check, of course) would look and behave very intuitively. This would also be very useful for singletons, which would use a private initializer for creating the instance and a public factory initializer for returning it.

3. Failable Member Initialization

The third idea is to allow writing `self.member = MemberType?(...)` or `self.member = .init?(...)` (the exact syntax is up to debate) inside failable initializers, which would be simply rewritten as:

guard let _self_member = MemberType(...) else {
return nil
}
self.member = _self_member

This will dramatically reduce the boilerplate and visual clutter form complex failable initializers that call other failable initializers. A good use case would be complex `LosslessStringConvertible` types with many `LosslessStringConvertible` members.

So, what do you guys think?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Gor Gyolchanyan) #5

Could you please elaborate why #1 and #3 would violate the rule?

About #1:

Currently, before all members are initialized, attempts to call to `super.init` or escape `self` will be met with a compile-time error.
The idea behind `self = nil` is to mark the initializer as `going to fail on return`, which simply fails the initializer upon return, while allowing members to be left uninitialized (which is exactly what happens if you `return nil`).
The only way to avoid failing the initializer at this point is to directly assign to self.
Seems like in both of these scenarios there are not uninitialized members because either the initializer fails, of `self` is assigned a fully-formed instance.

About #3:

The idea behind `self.member = MemberType?(parameters)` is to first call the failable initializer of `MemberType` and in case it fails, immediately fail the enclosing failable initializer which the member is being initialized in. This guarantees that this member is either initialized, or the failable initializer returns nil immediately.

路路路

On Jun 8, 2017, at 5:19 PM, David Sweeris <davesweeris@mac.com> wrote:

On Jun 8, 2017, at 05:09, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org> wrote:

Disclaimer: I do realize that any of the following ideas may have been discussed before and/or there might be a good reason for their lack of implementation, so please go easy in me. :slightly_smiling_face:

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.

2. Arbitrary `return` Statements In Intializers

The second idea is to allow `return ...` inside all initializers, which should also, theoretically, be a simple rewrite to `self = ...; return`. This one is to complement the existing `return nil` and allow some super-short initializers with a switch that returns in all cases or a more complex initializer that has a lot of guard statements.

2.1. Future Factory Initializers

In addition, the `return ...` syntax has the benefit for potential factory initializers. So far, the proposals for factory initializers involved a keyword attached to the initializer, which just complicates the lexical structure of the language and adds unnecessary complication to the interface of types. Currently, factory initializers imported from Objective-C or from C using the `__attribute__((swift_name("MyType.init(self:...)")))` look like normal initializers (an in case of C, the return value doesn't even have to be related to the enclosing type in any way), but behave as you'd expect: you call the initializer and the result is a value that *should* be a subtype of the type you've called the initializer for. So, if in the future Swift gets native factory initializers (including, most importantly, in protocols), it won't require special syntax, because simply returning an instance of a subtype (with a compile-time check, of course) would look and behave very intuitively. This would also be very useful for singletons, which would use a private initializer for creating the instance and a public factory initializer for returning it.

3. Failable Member Initialization

The third idea is to allow writing `self.member = MemberType?(...)` or `self.member = .init?(...)` (the exact syntax is up to debate) inside failable initializers, which would be simply rewritten as:

guard let _self_member = MemberType(...) else {
聽聽return nil
}
self.member = _self_member

This will dramatically reduce the boilerplate and visual clutter form complex failable initializers that call other failable initializers. A good use case would be complex `LosslessStringConvertible` types with many `LosslessStringConvertible` members.

So, what do you guys think?

#1 & #3 would violate Swift's rule about having to fully initialize all properties in inits.

My initial reaction is to like #2, though, assuming I understand it correctly.

- Dave Sweeris


(Gor Gyolchanyan) #6

Yeah, it's just a small syntactic sugar that doesn't change the current behavior at all, it merely makes initialization syntax more uniform and consistent.

Also, something just occurred to me:

When we're dealing with convenience initializers, there's the syntax `self.init(...)`, wouldn't it be nice to be able to use the same general syntax to initializing members?
The primary use case is to be able to avoid extra allocations and copies (especially for large structures).
It would look a lot like constructors in C++, where you have an opportunity to directly initialize members in-place, except it won't be mandatory in case the member is not default-initializable.
Here's an example of what I imagine it could look like:

init() {
聽聽self.myHeavyDutyMember1.init(...)
聽聽self.myHeavyDutyMember2.init(...)
}

The rule would be that if a direct member initializer call is present in an initializer, no access to the member is allowed before that call and no other direct member initializer calls on the same member are allowed, even if the member is mutable.

Also also, something else just occurred to me:

Currently Optional and ImplicitlyUnwrappedOptional are the only default-initializable types in Swift and that's compiler magic.
Wouldn't it be nice to have a DefaultInitializable protocol that the compiler would pick up on and treat the conforming type just like it does optionals? All the same initialization rules would apply.

protocol DefaultInitializable {

聽聽init()

}

An added benefit is how beautifully this would work with classes, where this will be a `required init`, meaning that subclasses will be forced to also be default-initializable, thus providing consistent polymorphic behavior that will make the initializability guarantees possible.

路路路

On Jun 8, 2017, at 6:00 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Jun 8, 2017, at 9:19 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

#1 & #3 would violate Swift's rule about having to fully initialize all properties in inits.

My initial reaction is to like #2, though, assuming I understand it correctly.

Assigning things to self can already be done with structs and enums:

struct S {
聽聽let foo: String
聽聽
聽聽init(foo: String) {
聽聽聽聽self.foo = foo
聽聽}
聽聽
聽聽init(bar: String) {
聽聽聽聽self = S(foo: bar) // works fine
聽聽}
}

enum E {
聽聽case foo
聽聽case bar
聽聽
聽聽init(isFoo: Bool) {
聽聽聽聽if isFoo {
聽聽聽聽聽聽self = .foo // works fine
聽聽聽聽} else {
聽聽聽聽聽聽self = .bar // ditto
聽聽聽聽}
聽聽}
}

The one restriction is that in the case of the struct, you can鈥檛 have initialized any of the immutable properties before assigning to self.

struct S {
聽聽let foo: String
聽聽
聽聽init(foo: String) {
聽聽聽聽self.foo = foo
聽聽}
聽聽
聽聽init(bar: String) {
聽聽聽聽self.foo = "Foo"
聽聽聽聽self = S(foo: bar) // error: immutable value 'self.foo' may only be initialized once
聽聽}
}

The only real thing being proposed here, as I see it, is to make this behavior conceptually consistent with classes. Anything that simplifies the rather Byzantine rules surrounding initializers, while simultaneously enabling factory initializers, is a win in my book.

Charles


(Greg Parker) #7

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.

路路路

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.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler


(Gor Gyolchanyan) #8

To clarify further about DefaultInitializable:

An uninitialized variable of a DefaultInitializable type will *only* have its `init()` called if it's accessed before being initialized manually. This will ensure that the variable will never be initialized twice.

路路路

On Jun 8, 2017, at 6:20 PM, Gor Gyolchanyan <gor@gyolchanyan.com> wrote:

Yeah, it's just a small syntactic sugar that doesn't change the current behavior at all, it merely makes initialization syntax more uniform and consistent.

Also, something just occurred to me:

When we're dealing with convenience initializers, there's the syntax `self.init(...)`, wouldn't it be nice to be able to use the same general syntax to initializing members?
The primary use case is to be able to avoid extra allocations and copies (especially for large structures).
It would look a lot like constructors in C++, where you have an opportunity to directly initialize members in-place, except it won't be mandatory in case the member is not default-initializable.
Here's an example of what I imagine it could look like:

init() {
聽聽self.myHeavyDutyMember1.init(...)
聽聽self.myHeavyDutyMember2.init(...)
}

The rule would be that if a direct member initializer call is present in an initializer, no access to the member is allowed before that call and no other direct member initializer calls on the same member are allowed, even if the member is mutable.

Also also, something else just occurred to me:

Currently Optional and ImplicitlyUnwrappedOptional are the only default-initializable types in Swift and that's compiler magic.
Wouldn't it be nice to have a DefaultInitializable protocol that the compiler would pick up on and treat the conforming type just like it does optionals? All the same initialization rules would apply.

protocol DefaultInitializable {

聽聽init()

}

An added benefit is how beautifully this would work with classes, where this will be a `required init`, meaning that subclasses will be forced to also be default-initializable, thus providing consistent polymorphic behavior that will make the initializability guarantees possible.

On Jun 8, 2017, at 6:00 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Jun 8, 2017, at 9:19 AM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

#1 & #3 would violate Swift's rule about having to fully initialize all properties in inits.

My initial reaction is to like #2, though, assuming I understand it correctly.

Assigning things to self can already be done with structs and enums:

struct S {
聽聽let foo: String
聽聽
聽聽init(foo: String) {
聽聽聽聽self.foo = foo
聽聽}
聽聽
聽聽init(bar: String) {
聽聽聽聽self = S(foo: bar) // works fine
聽聽}
}

enum E {
聽聽case foo
聽聽case bar
聽聽
聽聽init(isFoo: Bool) {
聽聽聽聽if isFoo {
聽聽聽聽聽聽self = .foo // works fine
聽聽聽聽} else {
聽聽聽聽聽聽self = .bar // ditto
聽聽聽聽}
聽聽}
}

The one restriction is that in the case of the struct, you can鈥檛 have initialized any of the immutable properties before assigning to self.

struct S {
聽聽let foo: String
聽聽
聽聽init(foo: String) {
聽聽聽聽self.foo = foo
聽聽}
聽聽
聽聽init(bar: String) {
聽聽聽聽self.foo = "Foo"
聽聽聽聽self = S(foo: bar) // error: immutable value 'self.foo' may only be initialized once
聽聽}
}

The only real thing being proposed here, as I see it, is to make this behavior conceptually consistent with classes. Anything that simplifies the rather Byzantine rules surrounding initializers, while simultaneously enabling factory initializers, is a win in my book.

Charles


(Gor Gyolchanyan) #9

Not exactly. #1 is an addition to the language syntax. It's simply a convenient way of expressing failure in a failable initializer the logic of which is already implemented, while extending the syntax in a way that would require zero effort to enable a potential factory initializers.

路路路

On Jun 8, 2017, at 6:45 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Isn鈥檛 1 und 2.1 almost the same stuff? I鈥檝e asked once if there is a chance for Swift to support init(_ other: Self) { self = other } for classes. Joe Groff replied this is called 鈥渇actory initializer鈥.

This feature is highly needed for all the iOS developers out there who abuse NIBs and create a custom UIView in a nib file then instead of assigning it to a UIViewController they nest it in another container view, which is not what it鈥檚 meant for in the first place. Factory initializer could solve this issue by simply assigning the instance created from a nib file to self. The nested view hierarchy would disappear and it won鈥檛 be that much of an abuse anymore.

--
Adrian Zubarev
Sent with Airmail

Am 8. Juni 2017 um 14:09:30, Gor Gyolchanyan via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

Disclaimer: I do realize that any of the following ideas may have been discussed before and/or there might be a good reason for their lack of implementation, so please go easy in me. :slightly_smiling_face:

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.

2. Arbitrary `return` Statements In Intializers

The second idea is to allow `return ...` inside all initializers, which should also, theoretically, be a simple rewrite to `self = ...; return`. This one is to complement the existing `return nil` and allow some super-short initializers with a switch that returns in all cases or a more complex initializer that has a lot of guard statements.

2.1. Future Factory Initializers

In addition, the `return ...` syntax has the benefit for potential factory initializers. So far, the proposals for factory initializers involved a keyword attached to the initializer, which just complicates the lexical structure of the language and adds unnecessary complication to the interface of types. Currently, factory initializers imported from Objective-C or from C using the `__attribute__((swift_name("MyType.init(self:...)")))` look like normal initializers (an in case of C, the return value doesn't even have to be related to the enclosing type in any way), but behave as you'd expect: you call the initializer and the result is a value that *should* be a subtype of the type you've called the initializer for. So, if in the future Swift gets native factory initializers (including, most importantly, in protocols), it won't require special syntax, because simply returning an instance of a subtype (with a compile-time check, of course) would look and behave very intuitively. This would also be very useful for singletons, which would use a private initializer for creating the instance and a public factory initializer for returning it.

3. Failable Member Initialization

The third idea is to allow writing `self.member = MemberType?(...)` or `self.member = .init?(...)` (the exact syntax is up to debate) inside failable initializers, which would be simply rewritten as:

guard let _self_member = MemberType(...) else {
return nil
}
self.member = _self_member

This will dramatically reduce the boilerplate and visual clutter form complex failable initializers that call other failable initializers. A good use case would be complex `LosslessStringConvertible` types with many `LosslessStringConvertible` members.

So, what do you guys think?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Gor Gyolchanyan) #10

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@apple.com> wrote:

On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto: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 gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler


(Adrian Zubarev) #11

Well I was referring to the title of (1) not to the addition it creates with failable initializers, which I guess is fine by me, but I鈥檇 need a more detailed proposal or draft to provide better feedback though.

路路路

--
Adrian Zubarev
Sent with Airmail

Am 8. Juni 2017 um 17:51:28, Gor Gyolchanyan (gor@gyolchanyan.com) schrieb:

Not exactly. #1 is an addition to the language syntax. It's simply a convenient way of expressing failure in a failable initializer the logic of which is already implemented, while extending the syntax in a way that would require zero effort to enable a potential factory initializers.

On Jun 8, 2017, at 6:45 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Isn鈥檛 1 und 2.1 almost the same stuff? I鈥檝e asked once if there is a chance for Swift to support init(_ other: Self) { self = other } for classes. Joe Groff replied this is called 鈥渇actory initializer鈥.

This feature is highly needed for all the iOS developers out there who abuse NIBs and create a custom UIView in a nib file then instead of assigning it to a UIViewController they nest it in another container view, which is not what it鈥檚 meant for in the first place. Factory initializer could solve this issue by simply assigning the instance created from a nib file to self. The nested view hierarchy would disappear and it won鈥檛 be that much of an abuse anymore.

--
Adrian Zubarev
Sent with Airmail

Am 8. Juni 2017 um 14:09:30, Gor Gyolchanyan via swift-evolution (swift-evolution@swift.org) schrieb:

Disclaimer: I do realize that any of the following ideas may have been discussed before and/or there might be a good reason for their lack of implementation, so please go easy in me. :slightly_smiling_face:

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.

2. Arbitrary `return` Statements In Intializers

The second idea is to allow `return ...` inside all initializers, which should also, theoretically, be a simple rewrite to `self = ...; return`. This one is to complement the existing `return nil` and allow some super-short initializers with a switch that returns in all cases or a more complex initializer that has a lot of guard statements.

2.1. Future Factory Initializers

In addition, the `return ...` syntax has the benefit for potential factory initializers. So far, the proposals for factory initializers involved a keyword attached to the initializer, which just complicates the lexical structure of the language and adds unnecessary complication to the interface of types. Currently, factory initializers imported from Objective-C or from C using the `__attribute__((swift_name("MyType.init(self:...)")))` look like normal initializers (an in case of C, the return value doesn't even have to be related to the enclosing type in any way), but behave as you'd expect: you call the initializer and the result is a value that *should* be a subtype of the type you've called the initializer for. So, if in the future Swift gets native factory initializers (including, most importantly, in protocols), it won't require special syntax, because simply returning an instance of a subtype (with a compile-time check, of course) would look and behave very intuitively. This would also be very useful for singletons, which would use a private initializer for creating the instance and a public factory initializer for returning it.

3. Failable Member Initialization

The third idea is to allow writing `self.member = MemberType?(...)` or `self.member = .init?(...)` (the exact syntax is up to debate) inside failable initializers, which would be simply rewritten as:

guard let _self_member = MemberType(...) else {
return nil
}
self.member = _self_member

This will dramatically reduce the boilerplate and visual clutter form complex failable initializers that call other failable initializers. A good use case would be complex `LosslessStringConvertible` types with many `LosslessStringConvertible` members.

So, what do you guys think?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Gor Gyolchanyan) #12

The problem is, it's not clear what assumptions the swift compiler may have on the semantics of initializers. There may be aggressive optimizations going on under the hood with the knowledge that the returned type is the static type that is being initialized. This guaranteed lack of polymorphism might be used to do things like in-place initialization when assigning to a variable or stuff like that. Factory initializers would break that logic and would require some additional compiler awareness, so as much as I'd love to have factory initializers, It doesn't look like it's as easy as adding syntax for it.

路路路

On Jun 8, 2017, at 6:53 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Well I was referring to the title of (1) not to the addition it creates with failable initializers, which I guess is fine by me, but I鈥檇 need a more detailed proposal or draft to provide better feedback though.

--
Adrian Zubarev
Sent with Airmail

Am 8. Juni 2017 um 17:51:28, Gor Gyolchanyan (gor@gyolchanyan.com <mailto:gor@gyolchanyan.com>) schrieb:

Not exactly. #1 is an addition to the language syntax. It's simply a convenient way of expressing failure in a failable initializer the logic of which is already implemented, while extending the syntax in a way that would require zero effort to enable a potential factory initializers.

On Jun 8, 2017, at 6:45 PM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

Isn鈥檛 1 und 2.1 almost the same stuff? I鈥檝e asked once if there is a chance for Swift to support init(_ other: Self) { self = other } for classes. Joe Groff replied this is called 鈥渇actory initializer鈥.

This feature is highly needed for all the iOS developers out there who abuse NIBs and create a custom UIView in a nib file then instead of assigning it to a UIViewController they nest it in another container view, which is not what it鈥檚 meant for in the first place. Factory initializer could solve this issue by simply assigning the instance created from a nib file to self. The nested view hierarchy would disappear and it won鈥檛 be that much of an abuse anymore.

--
Adrian Zubarev
Sent with Airmail

Am 8. Juni 2017 um 14:09:30, Gor Gyolchanyan via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

Disclaimer: I do realize that any of the following ideas may have been discussed before and/or there might be a good reason for their lack of implementation, so please go easy in me. :slightly_smiling_face:

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.

2. Arbitrary `return` Statements In Intializers

The second idea is to allow `return ...` inside all initializers, which should also, theoretically, be a simple rewrite to `self = ...; return`. This one is to complement the existing `return nil` and allow some super-short initializers with a switch that returns in all cases or a more complex initializer that has a lot of guard statements.

2.1. Future Factory Initializers

In addition, the `return ...` syntax has the benefit for potential factory initializers. So far, the proposals for factory initializers involved a keyword attached to the initializer, which just complicates the lexical structure of the language and adds unnecessary complication to the interface of types. Currently, factory initializers imported from Objective-C or from C using the `__attribute__((swift_name("MyType.init(self:...)")))` look like normal initializers (an in case of C, the return value doesn't even have to be related to the enclosing type in any way), but behave as you'd expect: you call the initializer and the result is a value that *should* be a subtype of the type you've called the initializer for. So, if in the future Swift gets native factory initializers (including, most importantly, in protocols), it won't require special syntax, because simply returning an instance of a subtype (with a compile-time check, of course) would look and behave very intuitively. This would also be very useful for singletons, which would use a private initializer for creating the instance and a public factory initializer for returning it.

3. Failable Member Initialization

The third idea is to allow writing `self.member = MemberType?(...)` or `self.member = .init?(...)` (the exact syntax is up to debate) inside failable initializers, which would be simply rewritten as:

guard let _self_member = MemberType(...) else {
return nil
}
self.member = _self_member

This will dramatically reduce the boilerplate and visual clutter form complex failable initializers that call other failable initializers. A good use case would be complex `LosslessStringConvertible` types with many `LosslessStringConvertible` members.

So, what do you guys think?

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #13

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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <gparker@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 gparker@apple.com Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Gor Gyolchanyan) #14

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, 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@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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <mailto: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 <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto: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 gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #15

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

路路路

On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor@gyolchanyan.com> wrote:

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@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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <gparker@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 gparker@apple.com Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Gor Gyolchanyan) #16

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@gmail.com> wrote:

On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor@gyolchanyan.com <mailto:gor@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@gmail.com <mailto:xiaodi.wu@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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <mailto: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 <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto: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 gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #17

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鈥檙e 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鈥檛 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 Fri, Jun 9, 2017 at 07:12 Gor Gyolchanyan <gor@gyolchanyan.com> wrote:

On Jun 9, 2017, at 2:07 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor@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@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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <gparker@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 gparker@apple.com Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Gor Gyolchanyan) #18

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

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.wu@gmail.com> wrote:

On Fri, Jun 9, 2017 at 07:12 Gor Gyolchanyan <gor@gyolchanyan.com <mailto:gor@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鈥檙e 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鈥檛 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.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor@gyolchanyan.com <mailto:gor@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@gmail.com <mailto:xiaodi.wu@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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <mailto: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 <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto: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 gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #19

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 鈥渟pecial,鈥 but it doesn鈥檛 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鈥檛 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鈥檛 like either of these explanations at all. I think
having a 鈥渟pecial鈥 IUO is a difficult sell; it is just conceptually too
complicated, and I don鈥檛 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鈥檚 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鈥檓 not excited about your idea #1.

Overall, I'd go with #2 because it involves much less confusing magic and

路路路

On Fri, Jun 9, 2017 at 07:33 Gor Gyolchanyan <gor@gyolchanyan.com> wrote:

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.wu@gmail.com> wrote:

On Fri, Jun 9, 2017 at 07:12 Gor Gyolchanyan <gor@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鈥檙e 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鈥檛 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.wu@gmail.com> wrote:

On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor@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@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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <gparker@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 gparker@apple.com Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Gor Gyolchanyan) #20

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.wu@gmail.com> wrote:

On Fri, Jun 9, 2017 at 07:33 Gor Gyolchanyan <gor@gyolchanyan.com <mailto:gor@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 鈥渟pecial,鈥 but it doesn鈥檛 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鈥檛 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鈥檛 like either of these explanations at all. I think having a 鈥渟pecial鈥 IUO is a difficult sell; it is just conceptually too complicated, and I don鈥檛 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鈥檚 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鈥檓 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.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Fri, Jun 9, 2017 at 07:12 Gor Gyolchanyan <gor@gyolchanyan.com <mailto:gor@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鈥檙e 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鈥檛 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.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Fri, Jun 9, 2017 at 06:56 Gor Gyolchanyan <gor@gyolchanyan.com <mailto:gor@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@gmail.com <mailto:xiaodi.wu@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鈥揵ut 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鈥檓 not sure that鈥檚 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?`鈥搘hich 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 <mailto: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 <gparker@apple.com <mailto:gparker@apple.com>> wrote:

On Jun 8, 2017, at 5:09 AM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org <mailto: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 gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution