[swift-evolution] [Proposal] Property behaviors

Brent Royal-Gordon brent at architechies.com
Thu Jan 14 15:20:28 CST 2016


> @autoclosure instead of `deferred` is an interesting idea. Also, as I mentioned to Felix, if we didn't have a way to bind the initializer, the same functionality could be achieved using custom accessors, at the loss of some sugar. If that's acceptable, it would greatly simplify the proposal to leave out the ability to hijack the initializer.

I think that might be acceptable for lazy, but doing it twice for resettable seems painful. So how about a compromise?

- There are only eager initializers.
- All behaviors have access to the initializer at any time by using `initializer`.
- The implicit stored property uses the initializer to initialize itself.

So `resettable` becomes a non-base behavior which uses `initializer`:

	var behavior resettable<Value>: Value {
		base var value: Value
		
		func reset() {
			value = initializer
		}
		get {
			return value
		}
		set {
			value = newValue
		}
	}
	
	class Foo {
		var [resettable] bar = 5
	}

Which means it can be properly stacked with observation behaviors. Meanwhile, `lazy` is a base behavior with an accessor:

	var behavior lazy<Value>: Value {
		var value: Value?
		
		accessor lazy()
		
		get {
			if let value = value {
				return value
			}
			let newValue = lazy()
			value = newValue
			return newValue
		}
		set {
			value = newValue
		}
	}
	
	class Foo {
		var [lazy] bar {
			lazy {
				return 5
			}
		}
	}

Hmm. Any chance we can get rid of the `[lazy]` when we see `lazy` in the accessor list? Such a feature might also allow us to de-magic `willSet` and `didSet`.

>> Do you think it makes sense to allow non-base properties to accept initializers? If so, what does it mean? What happens if one behavior wants deferred and another wants eager?
> 
> I think, fundamentally, only the innermost behavior can control initialization. If you innermost behavior itself has a base, then there's essentially an implicit "stored" behavior nested within it, which has the standard stored property initialization behavior.

That makes sense, but I think access to the initializer should probably be broader, as I suggested above. That is, only the innermost behavior initializes the storage, but all behaviors have access to the initial value for whatever use they might want to make of it.

(Although `lazy` would need to be able to say "this takes no initializer". Hmm...)

>> One more thing: should there be a way to pass arbitrary data, rather than initializers or accessors, into a behavior?
> 
> That parameterization could be achieved, somewhat more verbosely, using an accessor 'key { return "id" }'. If behaviors could be parameterized in-line, I wouldn't do it by passing the params to `init`, since that again imposes the need for per-instance storage to carry `key` from the behavior initialization to the property implementation.

If we were going to do that, I'd prefer to see accessors get the one-expression-return behavior of a closure:

	key { "id" }

This would also make using an accessor for `lazy` more palatable!

-- 
Brent Royal-Gordon
Architechies



More information about the swift-evolution mailing list