Renaming Trailing-closure Functions in the Standard Library

This feels like an unfortunate workaround for the fact that the first trailing closure doesn't permit being labeled. Why not change that, instead of changing the name of the function (and thus breaking the consistency of the Swift API naming guidelines as well)?

In the proposal review thread, @John_McCall wrote:

This isn't hypothetical; this thread and these standard library functions are the lived experience.

Another reason I would prefer a labeled closure vs. moving the label into the basename is something I raised in the initial proposal review: the syntactic ambiguity when using a trailing closure in a conditional statement. Today, we can't do the following, because the parser can't look ahead far enough to know whether this is a function with a trailing closure or a property access followed by the conditional body. This forces us to use an unnatural syntax for these methods, only when they occur in conditionals:

// error
if array.first { $0 < minValue } > 0 { ... }

// works, but unnatural
if array.first(where: { $0 < minValue }) > 0 { ... }
if (array.first { $0 < minValue }) > 0 { ... }

The proposed remedy won't fix this:

// still an error
if array.firstWhere { $0 < minValue } > 0 { ... }

However, if we had a way to require the label on the trailing closure, the parsing ambiguity would be eliminated; the presence of the colon following the label would unambiguously indicate that the subsequent { begins a trailing closure and not the conditional body. Furthermore, it lets us use the same syntax for any occurrence of this function call, regardless of surrounding syntactic context:

// good, reads well, consistent
if array.first where: { $0 < minValue } > 0 { ... }

That probably won't resolve the type checker performance issue for count, though. I don't have a great answer to that; one that comes to mind would be to require any function with a required trailing function label to be referenced using its full decl-name and not permit it to be referenced by its base name alone:

let x = queue.count  // excludes count(where:)
let x = queue.count(where:)

I'll be the first to admit that's a bit awkward, though. But I'd love to see a more holistic solution to this problem.

62 Likes