I've been thinking long and hard about Property wrapper's wrapperValue
. I came to believe that it's necessary, almost natural, to have, and I do have some suggestion:
- Make
$foo
usable only whenwrapperValue
is defined - Added
init(initialWrapperValue: WrapperType)
to allow initialization from$foo
, ie.$foo = WrapperType()
tl;dr: this would make a mental model of foo
always accessing Wrapper<...>.value
/wrappedValue
, and $foo
always accessing Wrapper<...>.wrapperValue
, which would be an easier mental-model.
Longer version:
I was trying to find the relation between Property wrapper, wrapperValue
and wrappedValue
. And I came up with a conclusion that Property Wrapper is the common storage for the tightly-coupled wrapperValue
and wrappedValue
. So the base storage is inaccessible on its own, but vent out 2 accessible variables, wrappedValue
and wrapperValue
.
The distinction between base storage and $foo
is rather important since there are cases where base storage is not accessible at all (due to wrapperValue
existence).
┌───────┐ @Wrapper var foo: Foo
|Storage|
└───┬───┘ // Synthesized
| var _base: Wrapper<Foo> // Inaccessible
wrapped | wrapper var foo: Foo {
┌────┴────┐ get { _base.wrappedValue }
| | set { _base.wrappedValue = $0 }
┌──┴──┐ ┌──┴──┐ }
│ foo │ │$foo │ var $foo: ... {
└─────┘ └─────┘ get { _base.wrapperValue }
↑ ↓ ↑ ↓ set { _base.wrapperValue = $0 }
access access }
foo
could be regarded as front-facing interface that is used for most access behavior (get
/set
), while $foo
is back-facing property that is used for the added extra behavior (Reset variable; add Binding, Link, Delegate, Observer, etc.).
We may not always want to have direct access to PropertyWrapper itself if the only useful thing you can do is $foo.alternativeRepresentation
, esp. since many PropertyWrappers will be in a form of one-off struct/class that's not very useful on its own, and those that are (useful on its own) don't even need direct access. But of course if direct access is wanted one can do
@propertyWrapper struct Wrapper {
var wrapper: Self {
get { return self }
set { self = $0 }
}
}
Then, all PropertyWrapper that doesn't need extra functionality can omit wrapperValue
, which will in turn disable $foo
access.
Furthermore foo
and $foo
syntactically should be proper variable in its own right. So we should allow for initializer to be done via $foo
, to which I suggest init(initialWrapperValue:)
.
init(initialWrapperValue:)
is needed because $foo
may not refer to its wrapper type (even in current pitch).
As per composition, foo
should work much the same way as in the current pitch, but $foo
has extra restriction, that only upto one (1) PropertyWrapper can have wrapperValue
. $foo
will be ill-formed otherwise.
So, here I conclude my thought process, that PropertyWrapper are one that wrapper around any normal type, and then provide extra functionality via $foo
. I tried to decouple PropertyWrapper and whatever $foo
is (getting B
object from A
variable, <SharedStorage?>), but in the end it seems like PropertyWrapper is almost a light extension of <SharedStorage?>. So (as implied in any internet comment), any thought?