Pitch: Property Delegates

Thanks for doing this. This is a very long alternate proposal, so I'm going to try to address the major points and---in doing so---frame them in terms of a delta on top of the property delegates proposal I've been developing so it's easier to reason about.

The automatic synthesis of the backing storage is fairly key to the usability of this feature. It's the common case, but I think it's reasonable to say that it shouldn't be the only case. In other words, if you want to explicitly specify the backing storage property, I think that's a reasonable extension to the model I proposed. I showed one possible syntax in my reply to Chris, and having separate declarations makes things like the existing lazy work. Riffing on your example a bit:

lazy var bazStorage: Delegate<Int> = Delegate(...) // okay
public var baz: Int by bazStorage

bazStorage is the backing property; declare it however you want. baz is the publicly-facing property whose getter and setter will be synthesized to refer to bazStorage.value. Doing this means we could drop the by public stuff, because if you want to publish the storage property, you can go ahead and write it. It also means that the default storage property with the $ name could remain internal-only, so it never leaks out into a different module.

You asked why my proposal contains the restriction that the delegate type must be generic with a single type parameter. It's so that the property can declare it's user-visible type (which is the most important thing) separately from the usually-implementation-detail behavior type, like so:

public var x: Int by Lazy = computeMe()

The most important thing is the var x: Int, because x has type Int for clients. That type should not be buried after the by, or require you to look into the delegate type. You have this example:

var baz: Delegate.Value

To me, that's not a great way to express the API: most clients of baz won't care what Delegate is, and indeed it's likely to even be completely hidden (because, say, it's a caching thing or other implementation-detailed indirection), so no user should have to "click through" to the definition of Delegate to get this information. Delegate might not even be public API.

FWIW, I'm not a fan of matching up names like this. If we're going to say that you can explicitly declare the backing storage property, great, but let's be explicit about it.

This is part of my original proposal; was it not clear or are you being thorough?

This is as reasonable extension. There's really no technical reason for the "no accessors at all" restriction in my proposal. We can loosen that, so you can write the accessors you want to (say, providing set but having get be synthesized) and it's plausible that willSet/didSet can work as well.

Yeah, I should have spelled out this restriction in the original proposal. It's in the latest version.

I'm having a hard time following this part of your proposal. You introduce an @initForSynthetization attribute that doesn't do anything; I agree that it isn't needed, but why is it there at all?

I also don't really know what problem you are solving in this part of your proposal. My proposal has two forms: a "direct" initialization of the delegate instance by putting parentheses after the delegate type, e.g.,

static var isFooFeatureEnabled: Bool by UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false)

and, when the delegate type can be initialized based only on the underlying value, an = syntax that goes through the delegate's init(initialValue:), e.g.,

var x: Int by Lazy = 17
// equivalent to...
var x: Int by Lazy(initialValue: 17)

If we allow the storage property to be explicitly specified, then the initialization of the storage would go on that declaration.

Doug