<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jan 21, 2016, at 10:16 AM, Michel Fortin &lt;<a href="mailto:michel.fortin@michelf.ca" class="">michel.fortin@michelf.ca</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">Le 20 janv. 2016 à 21:44, Joe Groff &lt;<a href="mailto:jgroff@apple.com" class="">jgroff@apple.com</a>&gt; a écrit :<br class=""><blockquote type="cite" class=""><br class=""><blockquote type="cite" class="">On Jan 20, 2016, at 6:12 PM, Michel Fortin &lt;<a href="mailto:michel.fortin@michelf.ca" class="">michel.fortin@michelf.ca</a>&gt; wrote:<br class=""><br class="">Le 19 janv. 2016 à 21:38, John McCall via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; a écrit :<br class=""><br class=""><blockquote type="cite" class="">One of my worries about this proposal in general is that you’ve listed out half-a-dozen different uses and every single one seems to require a new twist on the core semantics.<br class=""></blockquote><br class="">That's my general feeling too about this proposal. I just didn't know how to express what you said above.<br class=""></blockquote><br class="">I *did* somewhat strategically pick my examples to try to cover the breadth of different things I see someone wanting to do with this feature.<br class=""><br class=""><blockquote type="cite" class=""><br class="">To me this proposal feels like it's is about trying to find a solution to multiple problems at once. A new problem arise that looks like it could be solved by a behavior, so the behavior feature expands to accommodate it. It looks like the wrong approach to me. <br class=""><br class="">The correct approach in my opinion would be to try to make various parts of this proposal standalone, and allow them to combine when it makes sense. For instance, if you wanted to define a standalone feature for defining custom accessors that can be used everywhere, you wouldn't come with something that requires a behavior annotation at the variable declaration. You'll come with something simpler that might looks like this:<br class=""><br class="">custom_acccessor willSet&lt;T&gt;(newValue: T) { // define a custom accessor...<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>set { // ... by redefining the setter...<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>willSet(newValue) // ...inserting a call to the accessor here...<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = newValue // ...before calling the underlying setter<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class="">custom_acccessor didSet&lt;T&gt;(oldValue: T) {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>set {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>let oldValue = currentValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = newValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>didSet(oldValue)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class="">custom_acccessor willChange&lt;T&gt;(newValue: T) {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>willSet {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>if currentValue != newValue {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>willChange(newValue)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class=""><br class="">Then at the declaration point you just directly use the globally accessible accessor:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var myvar: Int {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>willChange { print("will change to \(newValue)") }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">This fulfills at least one of the use cases. Can't we do the same treatment to each proposed use cases and see if there are other parts that can stand on their own?<br class=""></blockquote><br class="">I considered this approach. It works for behaviors that don't need to control a property's storage and only change the property's access behavior. To be fair, that covers a lot of ground, including things like observing, NSCopying, resetting, and locking synchronization. We would still need a feature, which could certainly be a different one, to generalize annotations that control the storage policy for decorated properties, which could cover things like laziness, indirect storage, unowned/weak-ness, dirty-tracking, C-style atomics, and pointer addressability—basically, anything where a plain old stored property of the API type isn't sufficient. (You could even throw get/set in this bucket, if you wanted to be super reductionist.) Finally, there's the feature to add operations on a *property* independent of its *type*, which interacts usefully with both other features—you need a way to reset a resettable or lazy property; maybe you want to bypass a synchronized property's lock in one place, etc. We'd like to improve on the "classic" answer of exposing an underlying ivar or property in these cases.<br class=""><br class="">If you want to break it down in micro-features, I guess there are three here:<br class=""><br class="">1. Factoring out storage patterns,<br class="">2. Factoring out accessor patterns, and<br class="">3. Adding per-property operations.<br class=""><br class="">(1) tends to be tightly coupled with (2)—if you're controlling storage, you almost certainly want to control the accessors over that storage. And (3) is useful with both (1) and (2). If there are separate features to be factored out here, I think they're very entangled features.<br class=""></blockquote><br class="">No single language feature necessarily has to "control" the accessor. The accessor is really of a pile of "custom accessor code" wrapping one another, some in the variable declaration, some in the variable's behaviours, and so why not some others in a global accessor declaration? They just pile up on top of each other.<br class=""><br class="">Last post was about (2), so let's try to attack (1) using this approach. Basically, this is going to be the same thing as your behaviour proposal, minus `var` inside of it, minus custom accessors defined inside of it, minus functions inside of it (we'll revisit that at the end), and where the base property is implicit (`currentValue`) and the initializer is implicit too (`initValue`).<br class=""><br class="">I'll start with an "identity" behavior, a behavior that wraps a base property while actually doing nothing:<br class=""><br class="">// identity for type T creates storage of type T<br class="">storage_behavior identity&lt;T&gt;: T {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// regular initializer, works with eager or deferred initialization<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>init {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>// initValue is implicitly defined in this scope<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = initValue // initializing the storage<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>get {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return currentValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>set {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = newValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class=""><br class="">Sometime you need to force the initializer to be eager or deferred. So we can do that with a keyword, in which case for `eager` the `initValue` becomes available anywhere inside the behavior, and for `deferred` it becomes available everywhere outside of `init`:<br class=""><br class="">// identity for type T creates storage of type T<br class="">eager storage_behavior eagerIdentity&lt;T&gt;: T {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// initValue is implicitly defined in this scope<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// eager initializer (takes not argument)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>init {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = initValue // initializing the storage<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>get {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return currentValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>set {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = newValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class=""><br class="">Note at this point how this is basically the same syntax as with the `custom_accessor` examples from my last post, minus there is no accessor to call in `get` or `set`, plus you have the ability to affect the storage. The main difference for the user is that, since it's a behavior, you opt in to it by annotating the variable. Whereas in the case of `custom_accessor` you opt it by writing the custom accessor body inside of the variable's accessors.<br class=""><br class="">Ok, now let's write lazy:<br class=""><br class="">// lazy for type T creates storage of type T?<br class="">deferred storage_behavior lazy&lt;T&gt;: T? {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>init { <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>// defered initValue not available in this scope<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = nil // initializing the storage<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>get {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>if currentValue == nil {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = deferredInitValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return currentValue! // converting from T? to T<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>set {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = newValue // converting from T to T?<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class=""><br class="">Now let's make it atomic:<br class=""><br class="">storage_behavior atomic&lt;T&gt;: Atomic&lt;T&gt; {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>init {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = Atomic(initValue)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>get {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return currentValue.payload<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>set {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue.payload = newValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class=""><br class="">Note how I can avoid adding variables inside the behavior by just changing the underlying type to be some kind of wrapper of the original type. That makes the feature simpler.<br class=""><br class="">We can also write synchronized (for variables in a context where self is Synchronizable):<br class=""><br class="">storage_behavior synchronzied&lt;T where Self == Synchronizable&gt;: T {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>init {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = initValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>get {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return self.withLock {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return currentValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>set {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>self.withLock {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>currentValue = newValue<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class=""><br class="">Here is a minimalistic logging behavior:<br class=""><br class="">storage_behavior logging&lt;T&gt;: T {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// implied "identity" version of init, get, and set.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>willChange {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>print("\(currentValue) will change for \(newValue)")<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class="">}<br class=""><br class="">This last example is interesting: it shows that you can use a globally-defined accessor inside of the behavior; you don't have to define a `set` or a `get` to make it useful. This logging behavior is basically just a shortcut for defining a variable like this:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var myvar: Int {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>willChange {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>print("\(myvar) will change for \(newValue)")<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>@logging var myvar2: Int // way less boilerplate!<br class=""><br class="">So in the end, a storage behavior model defined like this brings to the table the `init` and the modified storage type. Otherwise it's just a nice and convenient way to avoid repeating boilerplate you can already write inside of a variable declaration. Which is great, because with less special rules the whole behaviour feature looks much more approachable now.<br class=""></div></div></blockquote></div><br class=""><div class="">You're glossing over some of the subtleties of initialization. Recall that a "plain old" stored property can be initialized out-of-line:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">func foo() {</div><div class="">&nbsp; var x: Int</div><div class="">&nbsp; ...</div><div class="">&nbsp; x = 1</div><div class="">}</div><div class=""><br class=""></div><div class="">struct Bar {</div><div class="">&nbsp; var y: Int</div><div class=""><br class=""></div><div class="">&nbsp; init(y: Int) {</div><div class="">&nbsp; &nbsp; self.y = y</div><div class="">&nbsp; }</div><div class="">}</div></blockquote><div class=""><br class=""></div><div class="">which creates complications for your 'init { }' model. If initializers chain the way you describe, then either storage_behaviors can never be initialized out-of-line, which would be a regression for didSet/willSet applications, or we need to gain the ability for definite initialization to turn 'x = 1' into an 'init' call. However, since storage_behavior can refer to its initializer anywhere, not only during initialization, the initialization assignment potentially has to *capture* the RHS value to be evaluatable later, which would be surprising. If a behavior wants to be able to override a superclass property, as didSet/willSet can do today, then initialization is out of the behavior's control. These are the factors that influenced the "base property" design in my proposal—if you want behaviors that compose, they really *can't* meddle in the underlying base property's initialization. These use cases can be addressed instead by your accessor modifier mechanism; however, if you try to break apart storage behaviors and custom accessors, then both features suffer—storage behaviors can't require accessors to parameterize behavior, and custom accessors can't introduce new storage if needed to apply their implementation over the underlying property.</div><div class=""><br class=""></div><div class="">-Joe</div></body></html>