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

Reply via email to