We should also clarify how types propagate through variables:
@FooBuilder
func foo(@FooBuilder closure: () -> Foo) -> Foo { ... }
// (() -> Foo) -> Foo
// NOT @FooBuilder (@FooBuilder () -> Foo) -> Foo
let a = ```
This would be akin to treating builder annotation as part of the function name, not function type. So maybe we'll need to permit it when referring to function names:
let a = foo(@FooBuilder _:)
We shouldn't allow builder annotation at non-argument location in the protocol requirement:
protocol Foo
// Bad Ok
// | |
@FooBuilder func foo(@FooBuilder _: () -> Foo) -> Foo
One reason is that we never permit implementation details in the protocol requirements, even for property wrapper, which is half-implementation half-interface*. It calls into question of how much should be allowed in a protocol declaration, which I think should only be information relevant to the caller.
Another, more important reason is that it's confusing to the reader to omit a builder. It's fine for the writer to look up the protocol requirement and add the appropriate signature (sans builder annotation). On the reader side though, it's not clear why this compiles:
extension Conformer {
func foo() -> Foo {
foo1()
foo2()
if condition {
foo3()
}
}
}
The reader needs to know that Conformer
conforms to a protocol, that it requires foo
, and that foo
is annotated with @FooBuilder
in the protocol requirement.
It's probably much less of a problem for well-known frameworks like SwiftUI. Even then, the fact that the obscurity of this depends on the framework popularity is somewhat unsettling.
* It could also be because of the implementation difficulty, nonetheless, that's the current behavior.