I have been thinking further about the compiler diagnostics for `delayed`. It might be interesting to consider making various compiler diagnostics available to any behavior rather than having a special case in the compiler for `delayed`.
Here are some examples: * By default properties with a behavior must be initialized in phase one just like normal properties. * Behaviors can opt-in to a relaxed requirement that the property must be initialized *somewhere* in the initializer, but not necessarily phase one. Delayed would opt-in to this. * Behaviors can opt-in to a requirement that the property *cannot* be set outside of an initializer. Delayed would opt-in to this. * Behaviors can opt-in to a requirement that the property *cannot* be set anywhere. A variation of lazy might opt-in to this. (clear would still work as it is part of the implementation of lazy) Allowing behaviors to specify diagnostic behavior like this would probably be *possible* in the ad-hoc proposal. However, it would probably be a lot more clear and elegant if we adopt the “behavior declaration” idea. Matthew > On Dec 21, 2015, at 11:23 AM, Joe Groff via swift-evolution > <swift-evolution@swift.org> wrote: > > I played around a bit with the idea of a special behavior declaration. I > think it feels a lot nicer, though it also feels like a much bigger language > change if we go this route. Inside the declaration, you need to specify: > - what accessors the behavior supports, to be implemented by properties using > the behavior, > - if the behavior controls storage, what that storage is, and what > initialization logic it requires, > - if the behavior requires an initializer, and whether that initializer is > used eagerly at property initialization or deferred to later, and > - what operations the behavior offers, if any. > > Here's a quick sketch of how a behavior declaration could look. As a > strawman, I'll use 'var behavior' as the introducer for a property behavior > (leaving the door open to 'func behavior', 'struct behavior', etc. in the > possible future). If you were going to reinvent computed properties from > whole cloth, that might look like this: > > var behavior computed<T> { > // A computed property requires a `get` and `set` accessor. > accessor get() -> T > accessor set(newValue: T) > > // Accessors for the property > get { return get() } > set { set(newValue) } > } > > lazy might look something like this: > > var behavior lazy<T> { > // lazy requires an initializer expression, but it isn't > // used until after object initialization. > deferred initializer: T > > // The optional storage for the property. > var value: T? > > // Initialize the storage to nil. > init() { > value = nil > } > > // Accessors for the property. > mutating get { > if let value = value { > return value > } > // `initializer` is implicitly bound to the initializer expr as a > // `@noescape () -> T` within the behavior's members. > let initialValue = initializer() > value = initialValue > return initialValue > } > > set { > value = newValue > } > > // clear() operation for the behavior. > mutating func clear() { > value = nil > } > } > > Some behaviors like `lazy` and `resettable` want to take control of the > storage to manage their semantics, but many behaviors are adapters > independent of how the underlying behavior behaves. These kinds of behavior > are easy to compose with other behaviors and to override base class > properties with. You could use inheritance-like syntax to indicate a > "wrapping" behavior like this, and commandeer `super` to refer to the > underlying property. For instance, `synchronized`: > > var behavior synchronized<T>: T { > get { > return sync { return super } > } > set { > return sync { return super } > } > } > > or `observing` didSet/willSet: > > var behavior observing<T>: T { > accessor willSet(oldValue: T, newValue: T) { } > accessor didSet(oldValue: T, newValue: T) { } > > get { return super } > set { > let oldValue = super > willSet(oldValue, newValue) > super = newValue > didSet(oldValue, newValue) > } > } > > If you want to refer back to the containing `self`, we could support that > too, and by treating behavior functions specially we should be able to > maintain coherent semantics for backreferencing value types as well. > Implementing `synchronized` with a per-object lock could look like this: > > var behavior synchronizedByObject<T>: T where Self: Synchronizable { > get { > return self.withLock { return super } > } > set { > return self.withLock { return super } > } > } > > (though the juxtaposed meanings of `super` and `self` here are weird > together…we'd probably want a better implicit binding name for the underlying > property.) > > -Joe > > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution