Support use of an optional label for the first trailing closure

request to wait for the proposal

I suggest we wait and let @xwu post his proposal before we start discussing. It may address some of the points people are bringing up.

Semi-related discussion about formatting/syntax representation

To make sure I'm not misunderstanding the request here: If what you mean here is that a formatter should take a function call that does not have the label and transform it to insert the label, that's infeasible. Formatters like swift-format use syntax-tree representations that stop at the parse stage, so they don't contain any type/semantic information. For a given function call with a trailing closure that lacks a label, it has no way to know what that label should be.

The formatter also can't use semantic information because doing so would require successfully compiling all dependencies before formatting, which is too burdensome a requirement for a code formatter.


I guess I was including linters as well here. Though I would hope that formatters would be moving more toward a richer parsing representation (where's SwiftSyntax?) that could preserve this information.

Please don't speculate until the actual proposal was posted!

I strongly second this. Guys calm down and please stay away from this thread until the draft proposal was published. It‘s worth nothing to jump on a speculation train right now, just a waste of everyones time.

Related discussion (reply to @allevato)

Going in the other direction, though, which I think was what @Jon_Shier has mentioned above, is imminently feasible!

I will post the proposal text in short order.

Support use of an optional label for the first trailing closure


This proposal extends trailing closure syntax by supporting the use of an optional argument label for the first trailing closure. When paired with changes in tooling behavior, this feature would promote the alignment of declaration and use sites without source-breaking changes.


Closure expressions in Swift have a lightweight syntax with several "optimizations" as described in The Swift Programming Language:

  • Inferring parameter and return value types from context
  • Implicit returns from single-expression closures
  • Shorthand argument names
  • Trailing closure syntax

These optimizations allow users to write out only the most salient details explicitly at the point of use, promoting clarity when used judiciously. A caller can take advantage of some or all of these optimizations in a variety of combinations. For instance, one may use trailing closure syntax while explicitly naming the return type.

In Swift, function arguments can have labels prescribed by the author of the function that clarify the role of that argument. For instance, the mathematical function known as atan2 in C/C++ is named atan2(y:x:) in Swift so as to emphasize the somewhat unintuitive order in which arguments must be passed to that function. However, in the case of trailing closure syntax, the first trailing closure is always written without any argument label. For example:

// Without trailing closure syntax:
[1, 2, 3].first(where: { $0 > 2 })

// With trailing closure syntax:
[1, 2, 3].first { $0 > 2 }

This syntax presents at least three significant limitations which have been present in every shipping version of Swift, which we'll explore in turn. In each of these cases, the present workaround is to abandon the use of trailing closure syntax. Indeed, the authors of Google's style guide describe almost exactly the same limitations and prohibit the use of trailing closure syntax under those circumstances. Unfortunately, users cannot then avail themselves of all the other benefits of trailing closure syntax (such as decreased nesting) which may be just as applicable to those use sites as they are elsewhere.

Exclusion from statement conditions

At present, the use of trailing closure syntax is excluded from if conditions and similar scenarios. This is sometimes diagnosed as a warning when the usage is merely confusable to humans but simple enough for the parser; in more complex scenarios, it is an outright parsing error:

if let x = [1, 2, 3].first { $0 > 2 } {
// Warning: Trailing closure in this context is confusable...
// Fix-it: Replace ' { $0 > 2 }' with '(where: { $0 > 2 })'

if let x = [1, 2, 3].first {
  $0 > 2
} {
// Error: Closure expression is unused
// Error: Consecutive statements on a line must be separated by ';'
// Error: Top-level statement cannot begin with a closure expression

In this case, there is one workaround for the user which does not require abandoning trailing closure syntax. Namely, the entire caller can be surrounded by parentheses instead:

if let x = ([1, 2, 3].first {
  $0 > 2
}) {
} // Prints "3"

Inability to disambiguate the intended matching parameter

Swift uses a "backwards scan" through function parameters to match an unlabeled trailing closure to a compatible parameter:

func frobnicate(a: () -> Int = { 42 }, b: (() -> Int)? = nil) {
  if let b = b {
    print(a(), b())
  } else {
    print(a(), "nil")

frobnicate { 21 }     // Prints "42 21", equivalent to...
frobnicate(b: { 21 }) // Prints "42 21"

A user must abandon trailing closure syntax in order to specify that the given closure expression is intended to match the parameter labeled a:

frobnicate(a: { 21 }) // Prints "21 nil"

If the author of frobnicate revises the function to take an additional parameter of optional function type, the behavior of frobnicate { 21 } will change:

func frobnicate(a: () -> Int = { 42 }, b: (() -> Int)? = nil, c: Any? = nil) {
  if let b = b {
    print(a(), b())
  } else {
    print(a(), "nil")

frobnicate(b: { 21 }) // Still prints "42 21", no longer equivalent to...
frobnicate { 21 }     // Now prints "42 nil"

This is not to suggest that APIs of the sort above are ideally designed. However, the language itself should, whenever possible, help API consumers write correct code even if API authors sometimes make questionable design choices.

A related limitation is the inability to disambiguate among two overloads which differ only by argument label if trailing closure syntax is used:

func frobnicate(ifSuccess: () -> Result<String, Error>) { /* ... */ }
func frobnicate(ifFailure: () -> Result<String, Error>) { /* ... */ }

frobnicate { .success("Hello, World!") }
// Error: Ambiguous use of 'frobnicate'
// Fix-it: Use an explicit argument label instead of a trailing closure to call
//   'frobnicate(ifSuccess:)'
// Fix-it: Use an explicit argument label instead of a trailing closure to call
//   'frobnicate(ifFailure:)'

Loss of meaningful words at the use site

Certain APIs have not been ideally designed for use with trailing closure syntax. In the standard library, for example, drop(while:) reads like a different function without its argument label:

let x = [1, 2, 3, 2, 1].drop { $0 < 2 }
print(x) // Prints "[2, 3, 2, 1]"

When there is only a single parameter, it can be straightforward for an API author to adapt to this limitation of trailing closure syntax by agglomerating the otherwise dropped words to the base name. Indeed, the API naming guidelines are now amended to suggest that authors take the issue into account for the first trailing closure.

However, making that same change would be more difficult in the case of APIs where the parameter in question is the last of several: there could be no straightforward base name that can satisfactorily substitute for an aptly labeled argument towards the end of the use site.

Moreover, when multiple parameters at the end of a parameter list are of function type, any of these could have its label dropped at the use site, since users may choose not to use trailing closure syntax for an arbitrary number of arguments (in order to preserve meaningful words, for example):

func frobnicate(_: [Int], excluding: (Int) -> Bool, transform: (Int) -> Int) {
  /* ... */

frobnicate([1, 2, 3, 2, 1].drop { $0 < 2 })
  { $0 < 2 } // We certainly don't want to leave this argument unlabeled!
  transform: { $0 * 2 }

frobnicate([1, 2, 3, 2, 1].drop { $0 < 2 }, excluding: { $0 < 2 })
  { $0 * 2 }

// So what can we accomplish merely changing the base name?
let frobnicateAfterExcludingByTransforming = frobnicate // 🤨

Addressing this issue at the level of API design could require more disruptive changes, such as altering the order of arguments. This is a nontrivial ask of API authors to work around a deficiency in the language itself. Therefore, this proposal aims to provide a solution to address the problem at its root.


Proposed solution

This proposal would extend trailing closure syntax by supporting the use of an optional argument label for the first trailing closure.

(As with subsequent trailing closures, the user can write _ to indicate that the trailing closure should match an unlabeled parameter, but the need for such an explicit spelling should be rare.)

This solution allows Swift users to address each of the limitations enumerated above without abandoning trailing closure syntax:

  • Users can move existing callers into statement conditions without creating any parsing ambiguities:

    x = [1, 2, 3].first where: { $0 > 2 }
    if let x = x {
      /* ... */
    if let x = [1, 2, 3].first where: { $0 > 2 } {
      /* ... */
  • In the setting of a frequently evolving API, or where the "backwards scan" rule may lead to an unintuitive result, users can specify explicitly which parameter should match the trailing closure:

    func frobnicate(a: () -> Int = { 42 }, b: (() -> Int)? = nil) { /* ... */ }
    frobnicate b: { 21 }
  • Where the label provides meaningful information, users can preserve that information for the reader even when API authors have not reworked their APIs (or cannot do so) specifically for trailing closures:

    let x = [1, 2, 3, 2, 1].drop while: { $0 < 2 }
      excluding: { $0 < 2 }
      transform: { $0 * 2 }
  • Finally, in cases where multiple trailing closures are involved and one closure is not clearly "more primary," users can apply their judgment to label all trailing closure expressions:

      get: { /* ... */ }
      set: { /* ... */ }
      state:  { $0.login }
      action: { AppAction.login($0) }
    // See:
      get:  { $ } 
      send: { Action.nameChanged($0) }
    // See:
    let isSuccess = result.fold
      success: { _ in true }
      failure: { _ in false }

By making the use of a label optional for the first trailing closure, source compatibility is maintained with all existing code.


One possible objection to the syntax proposed is that users are unused to function names juxtaposed with argument labels without the use of parentheses. Indeed, SE-0279 claims: "Many find this spelling unsettling."

Certainly, this generalization will lead superficially to a new appearance for use sites such drop while: { /* ... */ }. However, the use of unparenthesized argument labels has already been made possible with SE-0279. There is no reason to conclude that, beyond any initial reactions to novelty, the labeling of the first trailing closure would be any more unsettling than the labeling of the second trailing closure using the same syntax and rules.

Another possible objection to the syntax proposed is that it will lead to a proliferation of different styles among users, leading to inconsistency and a corresponding proliferation of dicta among linters and style guides.

There are several reasons not to fear such an outcome:

First, as recounted above, closure expressions already offer a number of "optimizations" to users, who can choose to include or omit various information for the sake of clarity at the use site. Experience shows that users have used these optimizations generally to good rather than ill effect. This proposal demonstrates a number of scenarios where this additional feature—which we could consider another syntax "optimization"—could similarly be used to good effect.

Second, where linters and style guides have recommended against the use of trailing closures, it has often been for lack of clarity at the use site. For example, Google's style guide prohibits their use when a function call has multiple closure arguments so that each can be labeled. In other words, best practices have not coalesced towards blanket requirements to use or not to use some syntactic form, but rather to recommend use in cases where the resulting code is clear and non-use in cases where the result is unclear. Providing the opportunity for labels to be used for all trailing closures would obviate many current style guide recommendations against the use of trailing closures.

Third, although what's proposed here is an optional feature, consistency can be obtained (and users can be spared any indecision) by consistent first-party tooling behavior, which will permit alignment of declaration and use sites without source-breaking changes.

Tooling behavior

Swift's first-party code completion and formatting tools can make use of the feature proposed here to drive alignment of declaration and use sites without additional syntax or source-breaking changes.

When a set of behaviors is adopted consistently across Swift's first-party tools, API authors gain the ability to reason about readability at the use site, and API consumers are unburdened from having to make style choices (while still retaining the ability to improve the clarity of use sites without waiting on API authors—should they so choose).

(Click to expand or collapse this subsection.)
For illustrative purposes, one possible set of behaviors that would accomplish that objective is outlined.
  1. Where there is only one possible trailing closure, prefer an unlabeled closure if the parameter itself is unlabeled:

    // Preferred:
    let sum = measurements.reduce(0) { $0 + $1 }
    // Not preferred:
    let sum = measurements.reduce(0) _: { $0 + $1 }
  2. Where there is only one possible trailing closure, prefer a labeled closure if the parameter itself is labeled:

    // Preferred:
    words.sort by: { $0 > $1 }
    let x = numbers.drop while: { $0 < 2 }
    // Not preferred:
    words.sort { $0 > $1 }
    let x = numbers.drop { $0 < 2 }

    This might be controversial because it would diverge from currently written code (although that code would still remain valid). The intention is to work in conjunction with (1) to align declaration sites with use sites, giving API authors control over which arguments are labeled by default (albeit not mandatorily enforced by the compiler). Where the API consumer deems the result to be verbose and unhelpful, they can delete the label.

    (One alternative here is to adopt a heuristic, at least for methods known to the compiler. In SE-0118, a great number of parameter labels for closures were reworked, with many standardized to by when another word or phrase was not more apt. Therefore, we could treat parameters labeled by as though they were unlabeled—not the most elegant of long-term rules, however.)

  3. Where there are multiple possible trailing closures and at least one of the parameters is unlabeled, deem the last such parameter to be primary; prefer writing the call site such that the corresponding argument is the first and unlabeled trailing closure:

    func when<T>(
      _ condition: @autoclosure () -> Bool,
      _ then: () -> T,
      `else`: () -> T
    ) -> T { /* ... */ }
    // Preferred:
    when(2 < 3) {
    } else: {
    // Not preferred:
    when(2 < 3)
      _: { print("then") }
      else: { print("else") }
  4. When there are multiple possible trailing closures, and none of the parameters are unlabeled, deem them to be equal in importance until such time as the API author reworks those labels; prefer writing the call site such that all of the corresponding arguments are trailing and labeled:

    // Preferred:
      get: { ... }
      set: { ... }
      receiveCompletion: { ... }
      receiveValue: { ... }
    // Not preferred:
    Binding {
    } set: {
    ipAddressPublisher.sink {
    } receiveValue: {
  5. In the setting of statement conditions, where (1) and (3) aren't possible, prefer surrounding the entire caller with parentheses over abandoning trailing closure syntax.


Detailed design

This proposal would modify the grammar, as already modified in SE-0279, by making the unlabeled expr-closure optional that precedes the remaining labeled trailing closures (identifier|keyword|'_') ':' expr-closure.

The grammar would be relaxed to allow the use of all-labeled trailing closures in the setting of statement conditions. The treatment of unlabeled trailing closures in that setting would remain unchanged.

The same rule regarding resolution of unescaped default: in favor of the keyword which currently applies to labeled trailing closures would be maintained and thereby also apply to a labeled first trailing closure.

The "backwards scan" rule for matching unlabeled trailing closures, as extended by SE-0279, would also be maintained. That is, a backwards scan through the parameters to bind all labeled trailing closures to parameters by matching labels would be performed. Then, if there is an unlabeled trailing closure, the same scan as has long been done for an unlabeled trailing closure would still be performed, starting from the last labeled parameter that was matched.

Source compatibility

This feature will be a purely additive change to the language and introduce no new grammatical ambiguities.

Effect on ABI stability

This feature will not have any effect on ABI stability.

Effect on API resilience

This feature will not have any effect on API resilience.

Future directions

A migration story

As the core team has said, trailing closures stand out as one of the few places where Swift allows the API consumer to decide whether or not to use a label. In general, these labels are regarded as part of the name of an API.

With the conviction that matching declaration and use sites by supporting labeled first trailing closures is the correct course of action, it would be tempting also to propose that the compiler should warn against label elision. From a technical perspective, diagnosing this usage would require trivial effort. A fix-it and a migration tool could lift all current usages seamlessly into a brave new world.

However, it would be prudent to approach the evolution of the language with a certain degree of humility afforded by a stepwise approach. Should our convictions be borne out by experience after implementation of this proposal, accompanied by adequate tooling, then API authors and consumers will naturally coalesce around a set of best practices and styles.

When API authors and consumers alike have had adequate time to adapt their code as a result of this proposal, then it becomes possible to consider one of two courses of action:

  • If, even after APIs have been reworked to account for this proposal, some still benefit from giving callers the flexibility to elide the label for the first trailing closure, while others benefit from use sites that always match their declarations, then we may wish to create an attribute to distinguish these two groups of APIs.

To choose a deliberately unsuitable name in the vein of @warn_unqualified_access, we might call this @warn_elided_trailing_label. If a more stringent approach is preferred, the compiler may even emit an error instead of a warning when the attribute is used.

  • If, ultimately, all or almost all APIs benefit from use sites that match their declarations, then we may move forward with an approach that diagnoses any instance of label elision (which does not include cases where the parameter is unlabeled, of course) with a warning and a fix-it.

  • If, ultimately, all or almost all APIs benefit from giving callers the flexibility to elide the label for the first trailing closure, then we would not need to take further action.

Alternatives considered

Removal of caller discretion in usage of labels

At Swift's present stage of evolution, a change that would only allow unlabeled trailing closures to match unlabeled parameters would be source-breaking for a very large number of existing use cases.

One might argue that labels could be made mandatory only in the case of multiple trailing closures. The rationale might be that, where there is more than one, there should be clarity as to "which one's what" in a way that's not necessary for single trailing closures.

However, even single trailing closures can need such clarification when there are multiple possible parameters that can match. Establishing a mandatory labeling rule only for multiple trailing closures would cause a divergence that is not self-evidently justifiable. Moreover, after multiple trailing closures are shipped as implemented for SE-0279, this too would be a source-breaking change.

Finally, even in that limited form, a mandatory rule would require the use of repetitive or unhelpful labels for "primary" trailing closures where API authors haven't yet reworked them, whereas this proposal would allow API consumers to delete those labels.

Instead, this proposal stakes out a claim that much of the benefit that can be gained from such a breaking change would be recovered in a source-compatible way by adopting this proposal and a consistent behavior throughout first-party (and, in the future, third-party) tools that default to omitting labels only for trailing closures that match unlabeled parameters. With time, label elision would cease to be the go-to option but rather a legacy feature supported for source compatibility reasons.

As detailed above (see "Future directions"), this proposal leaves room for future steps that would approach the same end result at a later stage of Swift's evolution. It should also be noted that there is precedent for code that's forbidden by Swift's grammar compiling anyway with a warning, as demonstrated above in the case of trailing closures used in statement conditions. It would be possible, thus, to deprecate label elision without actually breaking any existing source code by sticking to warnings instead of errors.

Typechecking changes

As the "backwards scan" rule can produce unintuitive results for matching unlabeled trailing closures to parameters, a source-breaking change in a future version of Swift could address the issue, making the results more intuitive. This would remain a possible avenue of exploration in the future, but it does not interfere with, nor preclude, the use of labeled trailing closures at the present time either to disambiguate for the compiler or to clarify for the human reader.

Indeed, the use of labeled trailing closures would minimize the extent of any deleterious effects of a source-breaking change to the typechecker that improves parameter matching for unlabeled trailing closures.


Matthew Johnson for close reading and suggestions.

Semi-related discussion about formatting/syntax representation

swift-format does use SwiftSyntax. That's what returns the parse tree representation.

SwiftSyntax uses the Swift parser as a dylib to parse a file/string of Swift source code and return a tree representation. It can only see what you have written in your source code. If you have this:

let x = [1, 2, 3].drop { $0 < 2 }

It has no way to know that the label while: is what you would have put there, because that requires type information from dependencies, and SwiftSyntax stops after the parse stage, before the semantic analysis/type checking stage. There's nothing to "preserve" because the information is not there in the first place.

Getting that information would require successfully compiling all of the project's dependencies and using something like SourceKit to query the API, but compiling an entire project to format it is not a realistic requirement.


As is abundantly clear from the review and acceptance threads, I am supportive of allowing labels on the first trailing closure. I think we should go a step further than this proposal though.

I think this direction would be reasonable as a step on the way to making this an error. If the community was willing to accept the churn this would cause (at our own pace, for now) I think it is a good direction. However, the Core Team doesn't appear to have an appetite for this level of change at this time.

I think introducing an attribute on functions that produces an error at call sites that elide a label is the next best direction. It would give library authors the same degree of control over trailing closure usage site that we have with regular function call syntax. It should be accompanied by a change to the API Design Guidelines recommending that this attribute be used any time a potentially first trailing closure has a label. This would make it clear that aligning declaration and usage is the recommended practice in Swift.

An attribute allows libraries to opt-in to usage site alignment at their own pace. Eventually, if the community broadly adopts this as a best practice we may be in a better position to require usage site alignment by default. If we do reach that point, the attribute could be deprecated and eventually removed.

We don't need to decide now whether the attribute is used as a final design or a step in a migration path. Either way, I believe it would provide a lot of value in delivering on the most important fundamental principle in the API Design guidelines: clarity at the point of use.


Hey folks (@Lantua, @wowbagger, @Jon_Shier, @jjatie, @anandabits, @allevato, @xwu and including myself), can we do one moderating thing please. This thread had a bumpy start, I wish that it wouldn't, so can we edit all our posts between the original announcement and the actual proposal in a way to not delete them but to hide them, so that the actual proposal moves up a little bit and the few posts at the begging would fade out? Feel free to pursue the discussion down-thread though.

This text will be hidden

I'd really appreciate if everyone would do that.


@xwu From my reading I think the following would be supported, but probably worth spelling out explicitly:

let item = [1, 2, 3, 4, 5].first() where: { $0 > 3 }

This also offers another resolution to the "juxtaposition of identifiers" issues noted in Objections.


Correct, there's no change in the existing rules regarding the optional parens in that scenario, which are and will continue to be allowed.


I think the second of these sentences is plausible only under the "can need" assumption of the first, and that we should also discuss making this "can need" scenario go away.

Specifically, imagine a future where the syntax rule is that the trailing closure label may be omitted for a particular function signature if there is only one possible function parameter that it could mean.

Now imagine a rule that prohibits functions from having signatures that differ only in the parameter label of a closure that satisfies the above rule. That is, two functions that have a single only one closure parameter cannot differ only in the label on that parameter.

For example, it wouldn't be possible for a type to have first(where:) and first(without:). Or first(where:) and first(_:). (I'm assuming all of those parameters are closures.)

Of course, this is source breaking, but I would argue that it's source breaking in a good way. There seems to be a lot of consensus that the construct is already confusing and actively harmful.

(Personally, I'd like "necessarily single" trailing closures to have no label, or to have an optional label, and all "potentially or actually multiple" trailing closures to require all labels.)

Proposal looks great to me. I'll be one those "unsettled" by the floating labels in the single closure case, but those should be easily removable. I'm not particularly motived by the single closure label examples, as I'm not sure where brings much of anything to functions like first or filter, but the multi label examples are much more motivating.


Thanks a lot @xwu for putting that proposal together. That's very well put and detailed, and I have to admit that I think that's a good way forward and probably the best solution to everyone's frustrations with SE-279.

I think we could indeed benefit later to give control to the API author to disallow first label elision at call site with an attribute like you suggested for future improvements but that indeed can wait until later and until we let experience show how this turns out with current design.

My main concern left with all that (SE-0279 + this) is the potential effect on parser performance; I hope that amount of leniency in closure syntax won't worsen compile time too much.

(PS: spotted a typo on your first post: in the very last snippet of that post you used func keyword even for the 2 call sites)


I want to add a couple of examples to clarify the behavior of the attribute I am proposing. We'll consider this example:

extension Result {
   func fold<T>(success: (Success) -> T, failure: (Failure) -> T) -> T { ... }
// under the proposal, this is allowed:
let wasSuccessful = result.fold success: { _ in true } failure: { _ in false }
// under the proposal, this is also allowed:
let wasSuccessful = result.fold { _ in true } failure: { _ in false }

With the attribute I propose:

extension Result {
   // this attribute enforces the use of labels at all call sites
   func fold<T>(success: (Success) -> T, failure: (Failure) -> T) -> T { ... }
// this is now required:
let wasSuccessful = result.fold success: { _ in true } failure: { _ in false }
// this now produces a compiler error:
// "`fold` requires trailing closure labels, `success` was elided:" 
let wasSuccessful = result.fold { _ in true } failure: { _ in false }

The require_trailing_closure_labels is a strawman, if you like this idea please suggest something better.


I never write first in trailing closure form specifically because I think the label adds crucial clarity at the call site. As others have mentioned, drop(while:) is an even better example of where the label is essential.


I have to disagree there. Those labels provide no additional context beyond what you get with first or drop, as they largely serve as conjunctions in the current naming. You'd need the other part of the phrase for them to really be valuable, like first(matching) or drop(allMatching). This is also why I think the labels should always be optional: once you learn what the closures do, the labels are superfluous. Of course, this really only applies to frequently used closures, and so should be left to the discretion of the user. To me, the attribute is only interesting in that it would prevent the automatic completion of the new multi closure syntax for specific APIs which don't support it well. Of course, users could get similar control through improved tooling, so I'm not sure which way I'd want to go there.


I think it's less important to be concerned about specific examples from the standard library and just realize that it's possible to write APIs where writing the current trailing closure syntax means you lose clarity. For example, if you have a function call with a single trailing closure argument and non-closure arguments preceding it, there is nowhere that you can place a label to clarify what the purpose of that closure is, and I don't consider the status quo or "don't use trailing closure syntax" to be satisfying.