[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