As a feature name, I feel like "property macros" is too generic---it both overpromises (I should be able to define a macro to do anything with a property) and says too little (well, what can I do with them?). That said, I agree that the #Lazy
syntax evokes macro expansion, which fits the way in which property delegates are used. It scales okay to the initialization case:
enum GlobalSettings {
#UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false)
static var isFooFeatureEnabled: Bool
#UserDefault(key: "BAR_FEATURE_ENABLED", defaultValue: false)
static var isBarFeatureEnabled: Bool
}
Others have suggested using attribute syntax (e.g., @Lazy
) or embracing the $
also for the declaration side ($Lazy
); I prefer it to those options because we've already set aside #
for things in this space. That said, it makes placing any modifiers (like public
to making the storage property visible) a bit awkward---if they go in the parentheses, they look like initialization arguments, but there's no other natural space for them.
We already have "multiple access control modifiers" via private(set)
, so the notion is not newly-introduced here. Moreover, we are declaring two entities---that's fundamentally how we describe this feature---and it is reasonable that authors could choose whether the delegate is part of their API or not.
Presumably, this means that #Forward
is another new feature, but it's using the same syntax as a property delegate. We'll need to be careful in this syntactic space. Also, I think the relationship expressed above is backwards: we want to define fooStorage
as a normal (stored?) property, and sugar the declaration of foo
that delegates to fooStorage
.
Syntax aside, I think your meta-point is reasonable: rather than trying to pack configurability of the storage property into the original property declaration, we could say that there is a syntax for "delegate to this other named property". With the by
syntax, this can fall out as a result of name lookup referring to a property:
public var fooStorage: UserDefaults<Int> = ...
public var foo: Int by fooStorage // okay, we found a property; delegate to that
With a #
syntax, we'd probably need something special, e.g.,
public var fooStorage: UserDefaults<Int> = ...
#delegate(to: fooStorage) public var foo: Int
Yes, we could have some kind of #storage(of:)
. to access the backing storage. I find that to be rather verbose for the use case, but perhaps I haven't sold the use cases well enough to motivate using $
. I'll see if I can do better in a revision of the proposal.
Doug