[swift-evolution] [Proposal] Partial initializers

Brent Royal-Gordon brent at architechies.com
Wed Jan 20 22:57:57 CST 2016


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

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

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

#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