[swift-evolution] [Proposal] Property behaviors
john.calsbeek+lists at gmail.com
Fri Dec 18 00:14:43 CST 2015
This language feature seemed complicated to me at first glance. Then I read through Kevin’s treatment, and now it seems tremendously complicated. :-)
Property behaviors seem to be moving in the direction of formalizing the language’s property model, in that they interact with the property’s interface (accessors and more), storage, inheritance, delegation (logging/willSet/didSet), polymorphism (covariance), and probably more. Since the scope is so large, maybe this proposal can be split into parts?
What kind of language features would be needed to implement the (language-implemented) behaviors that we have now, ignoring the compiler’s blessing of syntax to make the implementation transparent?
The obvious way to think of behaviors is structs that contain the property’s storage, with interface methods providing access. If we tried to implement everything in Joe’s proposal directly with generic structs, what problems would be hit?
The first problem is the initializer closure: capturing `self` is a bad idea, but if the initializer just took `self` as an argument (if safe to do so), then the initializer could simply be stored in the “property box” with the downside of increased storage. Making this type-safe seems to require that box type also be generic over the type of the containing object, which is interesting. Lazy<Value>, MemoizationBox<Value>, Delayed<Value>, and Resettable<Value> can be straightforwardly implemented this way.
Synchronized<Value> can be as well: if `self` is available to be passed to the initializer, then it is available to grab a lock from. Of course here we start hitting the composition problems: you can write either Lazy<Synchronized<Value>> or Synchronized<Lazy<Value>>. Like Kevin said, what you really want is dispatch_once-like behavior: a single control value which means “uninitialized”, “being initialized”, or “initialized”.
Observed<Value> is another hint of a problem: if I understand correctly (I am really a Swift newbie), willSet and didSet can be overridden. Modeling that by composing a struct seems very difficult. A list of closures? Or closures which call the next one in the chain?
Also, you potentially want to be able to override the *whole property*, which is also a problem since it involves storage changes. And what you may really want is to override the whole property except for the observers.
I guess I’m implying that it should be possible to implement something with similar semantics and performance characteristics to property behaviors in a library, neglecting all special syntax. That prospect alone seems very intimidating. I wonder if there is a set of language features that would allow a library implementation, while still allowing properties to be thought of as a single “thing”? Even if those features are never implemented, it might make it clearer what specific magic is required for property behaviors to function. A non-exhaustive list:
1) overriding storage in a subclass while still vending the original interface to people using the object via a super type or a protocol
2) methods in the composed type that are overridable in the container type (willSet/didSet)
3) initializer method/closure that neither captures `self` nor takes up space in the instance when stored (i.e. acts like a method of the container type)
4) the proposed safety when composing multiple boxes in potentially incompatible orders
I think exploring what those features would look like in isolation might be illuminating. Of course, they may turn out to be overly generic and only useful in the context of properties.
More information about the swift-evolution