SE-0380: `if` and `switch` expressions

I think I can reconcile myself to the basic concept, but reading the details of this proposal, I keep finding myself wishing it were better. I would like a feature that's strong enough that we could at least consider deprecating the ternary operator, but if expressions are much weaker on at least two fronts: their type inference is much less powerful, and their use in subexpressions is forbidden.

This example seems really awkward, and although you don't show it, I assume this restriction would also affect empty array and dictionary literals.

I have questions:

  • How certain are we that, at least for if statements, stronger inference is not workable? It's not immediately obvious to me why we can handle bidirectional inference for ternary operators but not for if expressions.

  • Have we experimented—either here or in the work on SE-0362—with a very limited set of rules to help with the most common type ambiguities or mismatches? For instance:

    1. Separately type-check each expression, skipping expressions that are just a nil literal, empty array literal, or empty dictionary literal. (If all expressions are skipped, fail.)

    2. If all remaining expressions type-check and their result types differ only in optionality, lift each one to the most-optional type. (If there are other differences in their types, fail.)

    3. If a nil literal was skipped and the result type is not optional, lift all expressions to a type that is one level more optional.

    4. Type check expressions skipped in step 1 with the contextual type determined by the previous steps.

  • Failing that, should we at least allow a trailing as cast to provide a contextual type in addition to a type annotation on the variable? This reads a little more fluidly to my eye:

    let x = if p { nil } else { 2.0 } as Double?
    

Are many of the "strange ones" associated with isExprBasic == true (that is, the places where the grammar doesn't allow expressions to have trailing closures)? If so, we could ban if and switch expressions whenever trailing closures are not allowed.

For what it's worth, I'd love to see statement-level implicit member expressions in result builders deprecated in Swift 5 and eliminated in Swift 6; they're not available consistently enough to actually be useful anyway. (In other words, I'm suggesting a syntactic use restriction in the result builder transform that covered implicit member expressions at the top level of a statement.)

If we did that, I think we could make a dot after an if or switch form an expression in Swift 6 mode, while requiring people to use parentheses or something in Swift 5 mode if they want to use it there:

(if showButton {
    Button("Click me!", action: clicked)
} else {
    Text("No button")
}
    .someStaticProperty)

This review has been pretty negative, so I'd like to clarify that I'm currently undecided on this proposal, not firmly opposed. But gosh, I wish it would give me more reasons to say "yes".


Housekeeping note:

How is this rule implemented? Is it part of the grammar, such that the parser will reject if and switch in other positions, or is it more of a syntactic use restriction?

If it is a grammatical restriction, I think I'd like to see how it would be implemented in the grammar. (This wouldn't be grounds for rejection, just a request for the proposal to be more explicit.)

20 Likes