I'm sorry for the confusion; trying to retain compatibility with the current design I forgot that such wrappers wouldn't have backing storage; they should be eagerly computed on each get and set. Taking that into account, @EnvironmentKey should be declared as such:
struct EnvironmentValue<Key : EnvironmentKey> {
// Constants are okay.
let key: Key.Type
// Takes in 'self', 'enclosingInstance' and 'wrapped';
// returns value of type 'Value'.
subscript(
enclosingInstance: EnvironmentValues,
wrapped wrappedKeyPath: KeyPath<EnvironmentValues, Key.Value>
) -> Key.Value {
get {
enclosingInstance[key]
}
set {
// EnvironmentValues actually have a mutating setter, but
// I don't won't to get into the discussion of enclosing-self syntax
enclosingInstance[key] = newValue
}
}
}
Is my enclosing-self, stateless-wrapper syntax clearer now?
What you describe sounds to me like lazy semantics, for which stateless wrappers should not, in my opinion, be used. Perhaps I didn't make that clear, but I view stateless wrappers as simple, lightweight wrappers –– probably without side-effects –– that should, in theory, be inlinable.
In my color example, @DelegatedProperty should ideally be equivalent to manually writing a computed property that gets and sets the corresponding color component (in this case, red). As for side-effects, this wrapper would act as a function that takes EnclosingInstance and Self and returns Value for its getter; likewise, the setter would take inout EnclosingInstance and Self. No side-effects occur in the wrapper's getters and setters accesses.
struct Color {
private var _components: (red: Double, green: Double, blue: Double)
@DelegatedProperty(\._components.red)
var red: Double
// === Becomes --------------------
var red: Double {
get {
DelegatedProperty(\._components.red)[
self,
wrapped: \Color.red
]
}
set {
DelegatedProperty(\._components.red)[
&self,
wrapped: \Color.red
] = newValue
}
}
// === Similar to -------------------
var red: Double {
get {
_components.red
}
set {
_components.red = newValue
}
}
}
Again, syntax for access of the enclosing-self has not been fully fledged-out meaning that @DelegatedProperty would not be valid (it would also have be declared with generic parameters: Root and Value.
The current limitations are not debilitating or unsolvable. In fact, if I recall correctly, it has been proposed that, instead of subscripts, enclosing-self access should use read and write methods, which would solve @EnvironmentValue's problem.
In regards to determining Root for @DelegatedProperty, I think we could make the compiler recognize the "enclosingInstanceType" parameter name in the initializer which, when bound to a generic parameter of the property-wrapper type, would help type inference understand that a given generic parameter should be inferred to be the enclosing-self type. Thus, when writing @DelegatedProperty(\._components) var red the compiler would infer that we mean \Color.red. If this approach for inferring generic parameters to be the enclosing-self type proves to be too cumbersome, an attribute would be another option worth considering.