That's a good point; this is something we can resolve by extending the existing rule to the last of the trailing closures in the list, and today it won't really matter because there's no way you could ever put a member on a function type. If that were to change, e.g., because I'm able to write an extension on a function type:
extension<P, R> (P) throws -> R {
func toResult() -> ((P) -> Result<R>) {
return { p in Result<R>(catching: { self(p) }) }
}
}
then it feels like I should be able to write:
foo(bar)
success: { ... }
failure: { ... try ... }.toResult()
but this will be parsed (incorrectly, for my purposes) as:
foo(bar,
success: { ... }
failure: { ... try ... }
).toResult()
Of course, the existing trailing closures would have the issue I describe. The proposal as written gives a us a way out when we go down the path of extensions to structural types. Maybe we won't care or need it.
Thinking more about this example:
foo(bar)
success: { ... }
failure: { ... }
It feels very like most of Swift's precedent is to have delimiters around the trailing closures. Computed properties work that way
var computed: Int {
get { ... }
set { ... }
}
Switch statements work that way:
switch x {
case .a:
// ...
case .b:
// ...
}
If-else has come up as a syntax we should emulate for multiple trailing closures, but if/if-else requires "extra" curly braces as well:
if x {
print("curly braces strictly unnecessary")
}
So, even though we can resolve the ambiguity in a way that works with the language today, and we might be okay with even when extensions of structural types come along, it feels like Swift leans more toward using curly braces to group things together when they're conceptually together. All those examples above could drop the extraneous curly braces (sometimes with small grammatical tweaks), but we didn't. It seems like those reasons apply here, too.
Doug