[swift-evolution] [Proposal] Partial initializers

Matthew Johnson matthew at anandabits.com
Thu Jan 21 13:58:33 CST 2016


> On Jan 20, 2016, at 10:57 PM, Brent Royal-Gordon <brent at architechies.com> wrote:
> 
>> Aside from that, how do you like the changes in this draft in general?  
> 
> (I'm also going to address some of Jordan Rose's comments here.)
> 
> I'm afraid I still have objections to the basic syntax:

How strong are your objections?  Would you vote against this because of the syntax or is it merely a preference for something different?

> 
> - I'm not a big fan of the calling syntax. The name on the left of the dot looks like it ought to be a type or maybe an object, but it's neither. It's also not parallel to the declaration—you declare `init foo()`, but you call `foo.init()`.

The thing I like about this syntax is that it emphasizes that initialization is happening and that you are not just making a normal method call.  

I do think a reasonable argument can be made to just use normal method syntax.  I mention this in the proposal and am willing to use the syntax that most people like best.

> - I don't like the fact that partial inits are distinguished from regular ones solely by whether or not they have a name. This is just not an obvious affordance.

That’s a fair opinion.  I will re-introduce the `partial` keyword if most people prefer that.  

I don’t think using `func` to declare something that writes to `let` properties is a good idea.  I very much prefer `init <name>()` or `partial init <name>()`.  

> 
> I really think that both of these problems can be avoided by reimagining this feature as a way to make methods that can be called during initialization. That is:
> 
> 	class Foo {
> 		var a, b: Int
> 		let now: NSDate
> 		
> 		init func resetValues(i: Int) {
> 			a = i * 2
> 			b = i * 4
> 		}
> 		
> 		init(only) func captureMoment() {
> 			now = NSDate()
> 		}
> 
> 		init(i: Int) {
> 			captureMoment()
> 			resetValues(i)
> 		}
> 	}
> 
> An `init(only)` method can initialize constants, but cannot be called after phase one. It would not be possible to make the partial initialization aspect of these calls public; `resetValues(_:)` would look like a normal method, and `captureMoment()` would not appear at all.
> 
> Alternatively, we could have the `init` keyword always take a list of the properties that will be initialized. This avoids the body-scanning that Jordan complained about, but it would not make explicit which init methods aren't available after initialization—you'd have to examine the property list and figure out which ones were constants. On the other hand, it at least theoretically permits the initializer aspect of the method to be made public, since the properties it initializes will be explicitly declared in the interface.
> 
> 	class Foo {
> 		var a, b: Int
> 		let now: NSDate
> 		
> 		init(a, b) func resetValues(i: Int) {
> 			a = i * 2
> 			b = i * 4
> 		}
> 		
> 		init(now) func captureMoment() {
> 			now = NSDate()
> 		}
> 
> 		init(i: Int) {
> 			captureMoment()
> 			resetValues(i)
> 		}
> 	}
> 
> One annoying thing about this syntax is that the property list in `init(foo)` looks like the parameter list in an initializer definition, but it actually means something totally different.
> 
> A third option here is to have a separate `initonly` keyword *and* require a declaration of initialized properties. This gives us the explicitness of `init(only)` and the in-declaration property list, but it's a bit ugly.
> 
> 	class Foo {
> 		var a, b: Int
> 		let now: NSDate
> 		
> 		init(a, b) func resetValues(i: Int) {
> 			a = i * 2
> 			b = i * 4
> 		}
> 		
> 		initonly(now) func captureMoment() {
> 			now = NSDate()
> 		}
> 
> 		init(i: Int) {
> 			captureMoment()
> 			resetValues(i)
> 		}
> 	}
> 
> Presumably you could omit the list of properties to write a side-effect-free helper method. This would have the same capabilities as the current static method approach, but with a more convenient calling syntax.
> 
> 	class Foo {
> 		var a, b: Int
> 		
> 		private init func double(i: Int) -> Int {
> 			return i * 2
> 		}
> 		
> 		init(a, b) func resetValues(i: Int) {
> 			a = double(i)
> 			b = double(double(i))
> 		}
> 		
> 		...
> 	}
> 
> As Jordan mentioned, overridability is a problem. I see two choices here:
> 
> 1. Allow overriding, but don't call the override during phase one initialization.
> 2. Make `init` imply `final`.

I think `init` *has* to imply `final`.  We cannot call a subclass method during phase 1, period.

> 
> #1 is kind of weird; #2 is very limiting. If we go with #1, we also have to decide if we want to allow `super` calls and how they should be handled during initialization, since they can't actually be executed. Perhaps they would be implicitly ignored, or you would have to make sure they weren't called on initialization using a special syntax:
> 
> 	class Bar: Foo {
> 		var c: Int
> 		
> 		override init(c) func resetValues(i: Int) {
> 			if !init {
> 				super.resetValues(i)
> 			}
> 			c = i * 8
> 		}
> 		
> 		override init(i: Int) {
> 			resetValues(i)
> 			super.init(i: i)
> 		}
> 	}
> 
> (On the other hand, perhaps #1 is an argument for your `foo.init()` syntax—it looks like a different kind of call, so you might expect it to have different semantics like not calling the override.)
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 



More information about the swift-evolution mailing list