[swift-evolution] [Review] SE-0030 Property Behaviors

Joe Groff jgroff at apple.com
Mon Feb 15 16:55:16 CST 2016


> On Feb 14, 2016, at 9:16 AM, Michel Fortin via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> What is your evaluation of the proposal?
> 
> I think property behaviors are a nice idea. But I feel the current proposal is trying to handle too many disparate use cases using an approach that doesn't work that well for all of them.
> 
> I already expressed my view of this in pre-review, and I'm a bit disappointed to see no mention of my suggestions in the "Alternatives considered" section. Basically I think this proposal should be broken in three or four separate smaller features that can each stand on their own:
> 
> 1. the ability to add `func`, `var`, and `let` inside any property declaration to handle metadata and meta-operations. For instance:
> 
> 	var count: Int {
> 		func reset() { count = 0 }
> 	}
> 
> 	count::reset() // or some other syntax
> 
> 	var root: NSObject {
> 		var lastModified: NSDate? = nil
> 		didSet { lastModified = NSDate() }
> 	}
> 
> 	print(root::lastAccess)
> 
> 	var lang: String {
> 		let xmlName = "xml:lang"
> 	}
> 
> 	element.addAttribute(name: lang::xmlName, value: lang)
> 
> 2. the ability to encapsulate those `func` and `var` inside a behaviour for reuse:
> 
> 	behavior resettable<T>: T {
> 		func reset() { currentValue = initValue }
> 	}
> 	var [resettable] count: Int
> 
> 	count::reset() // or some other syntax
> 
> 	behavior changeMonitored<T>: T {
> 		var lastModified: NSDate? = nil
> 		didSet { lastModified = NSDate() }
> 	}
> 	var [changeMonitored] root: NSObject
> 
> 	print(root::lastModified)

I think the whole space of out-of-band meta-operations deserves its own separate discussion. In addition to your concerns, exposing metaoperations as interface also introduces bigger concerns about resilience, protocol conformance, etc. that deserve deeper consideration.

> 3. the ability for the behavior (extending point 2) to change the base storage type of the property:
> 
> 	behavior synchronized<T>: Synchronized<T> {
> 		get { return currentValue.value }
> 		set { currentValue.value = newValue }
> 	}
> 
> 	var [synchronized] count: Int
> 
> 	count = 1
> 
> 4. a way to define property observers such as `willSet` globally, with no need to attach a particular behavior to a variable:
> 
> 	observer willSet<T>(newValue: T) {
> 		set {
> 			willSet(newValue)
> 			currentValue = newValue
> 		}
> 	}
> 
> 	var count: Int {
> 		willSet {
> 			print("\(count) will become \(newValue)")
> 		}
> 	}
> 
> I don't have a fully formed proposal, but there's a bit more details in a relatively recent discussion here (I changed the syntax a bit since then):
> 
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/007194.html
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/007238.html
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/007295.htm

As I noted in your pre-review comments, trying to separate storage control from observers doesn't seem like a net simplification to me. If you try to extend either feature on its own, you end up encroaching in the other's space. A behavior that manipulates storage may need per-declaration hooks, which accessor provides. An accessor may need to introduce bookkeeping storage.

Perhaps we could support inferring behaviors from accessor names, so that you can write `var foo: Int { didSet { ... } }` instead of `var [observed] foo: Int { didSet { ... } }`, but that seems like syntactic sugar over the basic functionality.

-Joe


More information about the swift-evolution mailing list