Multiple Trailing Closures

I understand :) The only problem I see is related to the dot, I think that has been proposed in the previous thread I mentioned as well.

The only variant which seems to not have been proposed yet is to have the label inside the closure:

try test(someCondition)
    {success: in ... }
    {failure: in ... }
     .get()

transition(with: view, duration: 2.0)
    {animations: in ... }
    {completion: in ... }

when(2 < 3)
    {then: in 3 }
    {else: in 4 }

It does not look nice for the when (which goal was to look like a standard statement) but for the other multi-trailing closures it could do the trick. The in is there as an attempt to avoid confusion with the block label with missing [code]do[\code].

1 Like

One of the nice things @xedin's proposed syntax does is syntactically delimit regular arguments and trailing closure arguments:

// before
UIView.animate(
    withDuration: 0.2,
    animations: {
        self.view.alpha = isEditing ? 1 : 0
    },
    completion: { _ in
        if !isEditing {
            self.view.removeFromSuperview()
        }
    }
)

// after
UIView.animate(withDuration: 0.2) {
    animations: {
        self.view.alpha = isEditing ? 1 : 0
    }
    completion: { _ in
        if !isEditing {
            self.view.removeFromSuperview()
        }
    }
}

I think this delimitation enhances readability similar to function builders in SwiftUI:

// without
VStack(
    alignment: .leading,
    Text("Bedtime"),
    Text("Every day")
        .foregroundColor(Color.gray)
        .font(.subheadline)
)

// with
VStack(alignment: .leading) {
    Text("Bedtime")
    Text("Every day")
        .foregroundColor(Color.gray)
        .font(.subheadline)
}
3 Likes

I’d actually prefer no commas or colons, so it’d be more akin to a var with a getter and setter:

when (2 < 3) {
  then { 3 }
  else { 4 }
}

or for another example in the comments:

func transition(with: view, duration: 2.0) {
    animations { [wipeAnimation] }
    completion { print(“i am complete”) }
}
1 Like

I believe the colons are necessary in order to avoid severe ambiguity problems with a trailing closure that happens to contain calls with trailing closures themselves.

3 Likes

Hi Pavel,

If you go way, way way back in Swift history, it was a goal to make control flow constructs be functions in Swift. This is where a lot of the ideas between autoclosures and other things came from. Would it be possible somehow to turn this example above into:

while (2 < 3) { 3 } else { 4 }

? This seems compatible with the "single trailing closures eat a keyword" syntax, and provides a very natural (though surely challenging to implement) "bareword" syntax for subsequent keywords for disambiguation. It doesn't seem any worse than our existing whitespace rules if considered carefully.

The bad thing about any of these is that return, break etc don't work "correctly" in closures. The solution to this would be (as an unrelated proposal) to implement a new "autoclosure like" attribute for function parameters that allows more expressive control flow in closure arguments.

1 Like
while (2 < 3) {
    3
} else {
    4
}

Seems worse to me than

while (2 < 3) {
    then: { 
        3
    } else: {
        4 
    }
}

I think that it is a mistake to try and pretend that user-provided functions end up blending in completely with control flow. It is at odds with another stated desire to avoid dialects. If we don't try to say that user constructs blend in perfectly with swift provided control flow, then the labels here both delimit the code and help the reader figure out where this 'control flow' structure is from (not directly from swift).

1 Like

Thank you, Chris! I think such syntax only makes sense in limited cases like while loop example but for APIs like transition it's less beneficial because such APIs don't have a control flow, so my thinking was that it would be beneficial for readability to group all of the trailing closures together and have regular parameters use parenthesis based syntax. IHMO that works well when closures become large like in SwiftUI I mentioned before and make other examples like while less syntactically heavy albeit not ideal.

Terms of Service

Privacy Policy

Cookie Policy