On the annotation at protocol requirement
The annotations at the protocol requirements should only be autocomplete hints instead of direct annotations. This makes them more inline with other decorations like parameter name. So when you declare:
protocol Foo {
@FooBuilder func foo(@FooBuilder _ result: () -> Foo)
}
The IDEs would suggest
struct A: Foo {
@FooBuilder func foo(@FooBuilder _ result: () -> Foo) {
// Uses Builder
}
}
At the same time, the following function can also satisfy the requirement:
struct B: Foo {
func foo(@FooBuilder _: () -> Foo) {
// DOES NOT use builder
}
}
In this case, whether or not the builder is used is still annotated at the declaration.
I'm strongly against making the leading @Builder
inferred from the protocol requirement. @Douglas_Gregor said that it's the same as inferring @Builder
for argument closures:
I'd argue that the two are actually different enough.
- The definition is readily available from the call site (with tooling).
- OTOH, one can't easily get to protocol requirement from the declaration site.
Furthermore, there actually isn't a strong 1-1 link between a function definition and a protocol requirement. So jump to Protocol Requirement doesn't actually make much sense.
I'm quite concerned about this since we can add infer from requirement but cannot remove them. More so that annotation actually signifies two things:
- that the builder is being used, and
- which builder is being used.
We can pretty much know when a builder is used (which was the premise for omitting the annotation at call site), but we don't know exactly which builder is being used. So the builder annotation should be easily accessible, even via tooling.
So I think making protocol requirement annotation an IDE hint would leave us in a pretty comfortable place that we can wait and see if we should add infer from requirement.
On the buildEither
I think that the best behavior for buildEither
would be to separate them from buildOptional
allowing the fourth scenario with buildEither
without buildOptional
. This would decouple the absence of value (buildOptional
) from the selection among multiple values (buildEither
).
The other option would be to require both buildEither
and buildOptional
for all branching statements to work.
I'm not too concerned about this though. It seems we'd end up in a relatively good spot that can additively include buildEither
without buildOptional
later (we might even mildly want to remove the case buildOptional
without buildEither
).
On the naming
If we look at the closure as a descriptor for the return value *Builder
does seem appropriate. ViewBuilder
builds a single view from the provided block. That may be a perspective we can use to come up with the most fitting name.