On the distinction between direct passing and via initializer, it's important (and potentially useful), but it'd be prudent to give the choice of that distinction to the function author, not the function user.
If we instead treat them as two different annotations:
func x1(@passByStorage @Wrapper a: Int)
func x2(@passViaInitializer @Wrapper a: Int)
x1(a: Wrapper(...))
x2(a: 0)
it gives the author the ability to choose between the two. We can then assign appropriate defaults:
struct WithInit { /* has init(wrappedValue) */ }
struct NoInit { /* no init(wrappedValue) */ }
func bar<T>(_: T.Type, _: (T) -> ())
func foo1(@WithInit a: Int) // @passViaInitializer
func foo2(@NoInit b: Int) // @passByStorage
// Type check, (generally unambiguous)
bar(Int.self) { (@WithInit a) in } // @passViaInitializer
bar(WithInit.self) { (@WithInit a) in } // @passByStorage
Then the unapplied method reference could follow the annotation:
let a = foo1 // (Int) -> ()
let b = foo2 // (NoInit) -> ()
let c = { (@WithInit a) in } // Depends on the "default" passing
let d = { (@NoInit a) in } // (NoInit) -> ()
It'd follow the progressive disclosure ideology. It'd also fit well with the member-wise initializer.
struct A {
@WithInit var a: Int
@NoInit var b: Int
}
// Generates
extension A {
init(@WithInit a: Int, @NoInit b: Int) { ... }
}
In the current behaviour, A.init
simply becomes (WithInit, NoInit) -> ()
, not (Int, NoInit) -> ()
.
We definitely could use more motivations. The proposal itself mentioned Binding
, State
, and Published
(in passing), but none of these wrappers are well-suited for this proposal; State
and Published
are entity-bounded wrappers and Binding
doesn't have init(wrappedValue)
. This shows that not every wrapper would be appropriate for this syntax. So it'd be helpful to at least find a class of wrappers where this feature would be useful upon. The proposal extensively uses LowerCased
, but, well, a single wrapper doesn't say much about its usability esp. if the reader is skeptical of the proposed form.