Improving the grammar for params inside multiline closures

Swift has a remarkably simple, orthogonal and intuitive grammar (especially considering the scope of the language). Swift's closures are exceptionally nice, except for an awkward edge-case: Closures with explicit parameters only really work well when the body of the closure is a single statement, on the same line as the params. For example, this kind of thing is nice:

upload(fileURL) { success in print("uploaded file: \(success)") }

delay(by: 2) { [weak self] in self?.showTutorialIfNeeded() }

As soon as you move the body to its own line, the parameters look funky:

upload(fileURL) { success in
    print("uploaded file: \(success)")
}

delay(by: 2) { [weak self] in
    self?.showTutorialIfNeeded()
}

I've experimented with putting the params on their own line:

upload(fileURL) {
    success in
    print("uploaded file: \(success)")
}

delay(by: 2) {
    [weak self] in
    self?.showTutorialIfNeeded()
}

I think this is a little nicer, but it's difficult to go against such an established convention. In any case, it'd be much better if we could put the params before the opening brace when the body of the closure is not on the same line.

In these examples, the grammar is identical, except that the in keyword and opening brace have swapped places:

upload(fileURL) in success {
    print("uploaded file: \(success)")
}

delay(by: 2) in [weak self] {
    self?.showTutorialIfNeeded()
}

To be honest, I wouldn't care about something as small as this in most languages, but Swift's grammar is so close to perfect, it seems worth trying to address the few remaining issues.

What do you think?

Seems backwards to me. The current syntax reads like (mostly) grammatical English: "capturing x, y, z into rest of closure". My biggest complaint about closures is that the autocomplete is pretty bad. 10 years on and it still doesn't seem to recognize capture lists or the in keyword.

5 Likes

It's true that in isn't the best keyword. I considered of, as it reads better in many contexts, but for better or worse, in is the established keyword in Swift. Therefore, it's much easier to effectively allow swapping the keyword and the opening brace (keeping the grammar otherwise unchanged) than to introduce a new keyword for something like this.

It's basically in or nothing.

I don’t really see the issue. Are you saying you think the parameters need more emphasis?

I kinda prefer the C# style => operator to in for single statement closures but it feels ugly and inconsistent anywhere else, whereas Swift's in syntax seems mostly ideal everywhere.

Moving it outside the braces doesn't make any sense imho, and reads really strangely in many cases, particularly when the closure is the first argument, or you can't use trailing closure syntax. Many standard lib methods like reduce, sorted and first(where: ) read quite awkwardly with that syntax also, just from a 'does it sound like English' point of view.

1 Like

I actually do prefer the C# version. In Swift, having the parameters inside the braces, I feel like they get lost visually. (I also prefer putting them on their own line because I don't like long lines)

On the other hand, this would be a very intrusive change, and since I've never seen anyone complain about this before I don't think it would be well received.

2 Likes

Some good points against the grammar I suggested.

I'm apprehensive about =>, just because closures often include their signatures (especially in docs that explain Swift closures), and the signatures often include -> already, so it may get a bit cryptic.

Maybe the answer is just to advocate for putting the params on their own line, and see how many people we can convert over time.

Not really. To be honest, I just think they often look awkward and gimmicky, as though Swift got too clever and missed an edge-case, so we're stuck with these weird looking parameters after the opening brace forever.

The examples I gave don't illustrate the worst cases. I wanted to keep them short and simple.

If we just started putting the parameters on their own line, I'd be content with that.