Pitch: Double (or more) trailing closures

A simple solution to the ambiguity problem of multiple ‘trailing’ closures is to simply require that they start on the same line as the preceding closure, after the }. e.g.:

// This is a single call to when(_:_:otherwise:)
when (2 < 3) { doSomething() } otherwise { do SomethingElse() }

// This is two independent calls, one to when(_:_:), the other to otherwise(_:).
when (2 < 3) { doSomething() }
otherwise { do SomethingElse() }

Or if you prefer:

// Again, unambiguously all a single statement & function call.
when (2 < 3) {
  doSomething()
} otherwise {
  doSomethingElse()
}

// Two independent statements & function calls (though it would arguably
// be stylistically superior to put a blank line between them, for further clarity).
when (2 < 3) {
  doSomething()
}
otherwise {
  doSomethingElse()
}

This builds on the notion that Swift implicitly uses line returns to signal the end of a statement, in lieu of an explicit semicolon. It also still leaves you the option of using an explicit semicolon to differentiate the scenario where you really do mean to start a second statement, not pass an additional, optional argument.

The compromise it makes is that it rules out the style choice of placing line-returns after the }, but I feel that’s fine since that’s atypical style in Swift, and if it comes down to a choice it’s more in line with idiomatic Swift than requiring special look-ahead parsing, semicolons on argument names, etc. It also ensures you can precisely mimic existing control flow syntax, if that’s your thing.

Mechanism aside, and thinking about whether Swift should adopt such functionality… I am intrigued by this. It does have some obvious concerns that would need assuaging, such as that it won’t end up being a C++ templates situation where people get carried away and produce confusing pseudo-DSLs and generally over-designed monstrosities. I’m also not all that interested in the intellectual amusement of replicating existing control flow structures by other means - that’s cute, but doesn’t seem practical.

However, I am interested in it being a cleaner way to express the very common patterns involved in using futures & promises, asynchronous / conditional control flow, and “N+1” call sequences (i.e. call this first closure for each of the N iterations / things / whatever, then this second closure once at the end).

It must be noted however that, unless variadic arguments of closures are also supported, this doesn’t seem to scale as well for … }.andThen { … }… sort of things.

2 Likes