Hi all,
Based on the discussions in the property delegates pitch and review, it seems that we should push further on the composition angle. So, I had an idea that could use some refinement.
Let's start with the notion that we have two trivial property delegate types, A
and B
:
@propertyDelegate
struct A<T> {
var value: T
}
@propertyDelegate
struct B<T> {
var value: T
}
We could allow multiple property delegates to be composed. That composition effectively has to be linear, nesting one delegate instead the other. The synthesized getter/setter would look through the full chain of .value
, e.g.,
@A @B var foo: Int
becomes:
var foo_storage: A<B<Int>>
var foo: Int {
get { return foo_storage.value.value }
set { foo_storage.value.value = newValue }
}
How do we get to the API for A
and B
? Well, the prefix $
from the proposal could be repurposed to mean "suppress one .value
", so $foo
refers to the foo_storage.value
(i.e., the B<Int>
) and $$foo
refers to foo_storage
(i.e., the A<B<Int>>
) directly.
You could take this one step further, such that value
members that themselves have attached delegates could be considered to be composed. For example:
@propertyDelegate
struct C<T> {
@B var value: T
}
@C var bar: Int
we could say that bar
is translated to:
var bar_storage: C<Int>
var bar: Int {
get { return bar_storage.$value.value }
set { bar_storage.$value.value = newValue }
}
Then, $bar
is the B<Int>
and $$bar
is the C<Int>
. It gives the same expressiveness as the delegateValue
property in the current proposal, but bases it on the same "suppress .value" unwrapping mechanism as composition.
I think I like this approach because it keeps the conciseness of $
for the common case (no composition, no tricks), allows composition while maintaining a direct translation model (nesting + multiple .value
s), and makes the delegateValue
effect not feel like a one-off trick.
I haven't tried to hack up an implementation, but it seems straightforward. Thoughts?
Doug