Principles for Trailing Closure Evolution Proposals

I share some of the concerns touched on by @DevAndArtist just now. I'll elaborate below.

I appreciate that this conversation is happening right now. I learned a lot by reading over the OP in this thread that will make me a better API designer period. To frame up the following, I am generally in favor of the syntax settled on for multiple trailing closures. That said, I think it is incumbent upon us (the community and the core team and language maintainers) to move quickly on follow-ups for trailing closures because multiple trailing closures have already been accepted but we don't yet have a good solution to the call-site clarity or API designer control over call-site legibility.

My thesis is that the middle-ground we have now is going to result in very real downsides in the short term and I am not convinced we can clean those downsides up easily because APIs will be designed around what we have today, not what we hope to have in the future, and APIs will be used the best they can be despite future changes potentially resulting in breaking changes to those call-sites. Note that here I am not referring to breaking changes mandated by Swift Evolution, but rather breaking changes to library APIs as library authors gain hypothetical tools like attributes that force labels to be explicit.

I think it is reasonable to say that multiple trailing closures do not actually maintain the status quo even if they ostensibly follow the same rules as single trailing closure already did. Where before an API designer needed to assume that the caller might omit the argument label for the last closure, now the problem has multiplied to the API designer needing to assume that any of the argument labels for the n last closures might be omitted; note, I am not saying that all of the arguments can be omitted, but rather that any one of them might be, depending on where the call-site begins using trailing closures. This was illustrated well here under the details of Jordan's "more nebulous note."

It is really unfortunate to be faced with the questions: Do I design my API with the best possible multiple trailing closure experience or do I design my API so that call sites are never confusing? Is it responsible of me to recommend trailing closure syntax (by way of example code) in my documentation given the fact that some call sites will benefit and others will not?

I have already experienced this problem in designing my own APIs in the past weeks and trying to think about how they will interact with multiple trailing closure syntax. It puts us in an awkward position: We can design something that works really well sometimes and not very well at all other times and it is not even strictly speaking always a matter of the end-user's stylistic preference. I'll give a bit of a silly example in the expansion below.

Example

I have a function with the following signature:

public func validate<T>(
    _ description: String,
    check validate: @escaping (ValidationContext<T>) -> Bool,
    when predicate: @escaping (ValidationContext<T>) -> Bool
)

Multiple trailing closures can make the call-site quite pretty (in my opinion):

validate("Two Ones Make a Two") {
    Two($0.oneValues) != nil
} when: { 
    $0.oneValues.count == 2
}

However, if complicated validations start to become smart to write as named closures, the call-site loses the benefit of explicitly labeling the last closure as "when" unless the user decides to arbitrarily hold themselves to the rule that "calling validate() with one trailing closure is disallowed but calling it with two trailing closures is preferred":

let allOnesAdd: (ValidationContext<T>) -> Bool = ...

validate("All Ones Add Up", check: allOnesAdd) { 
    $0.oneValues.count > 0
}

Obviously, this is analogous to the existing decision an end-user must make of whether or not to use single trailing closures based on call-site clarity, but now as an API designer I can no longer just "assume any given user will not use the last closure argument label." Instead I must assume that "some users will use it and some users won't" and "some users will prefer to use it or not depending on the circumstances."

19 Likes