Pitch: if/else expressions

(TJ Usiyan) #61

To try a different tact than "if we change one then we should change the other"

Properties on enums are a stellar example of where "switch as expression" would be an improvement.

2 Likes
(Davide De Franceschi) #62

To be fair, I would greatly welcome it. When put together with catch it is basically another conditional flow and as such has similar issues as if and switch

(Chéyo Jiménez) #63

If switch and if/else statements are so closely related why do we have them as separate constructs? Why is there an if let but not a let switch? Do people complain about let switch or switch let not being in the language?

Filibustering if/let expressions will end up further delaying all expression efforts. Last time this bill was killed was dec 2015. I urge the house to support this limited expression bill so it can go to the senate for voting.

(Matthew Johnson) #64

In fact switch statements do let you bind variables in the case patterns so of course users don't complain. The feature they need exists already.

I'm not filibustering at all, we're having a discussion. If this proposal was up for review I would urge the core team to accept it with an amendment to include switch expressions, or possibly accept it with an indication of interest in pursuing that direction as a followup in the near term. But I think it would be pretty awkward to have one and not the other in the language for an extended period of time.

5 Likes
(Ben Cohen) #65

I disagree. Failing to keep parity between if and switch statements would make the language worse. Incremental change can sometimes be a good way of making progress, but it can also lead to getting stuck in local minima.

Aside from not introducing an inconsistency, another argument for parity is much like the justification for making if statements expressions presented above. That is, developers may choose to make statements that are better expressed using switch into if statements purely because then they can be expressions, much like they use ternary expressions today instead of if statements.

Personally, I'm mildly in favor of single-expression if and switch statements expressions, but only if they are done together (and if it doesn't come at a cost to things like performance type checking of expressions). I'm opposed to going further and making all statements expressions. I don't think "we can't make everything an expression so let's just do it bit by bit" is a good enough reason to consider if and switch separately.

7 Likes
(Dave Abrahams) #66

Interesting. I think you have a realistic enough view of things to realize that you'd be encouraging acceptance with no ironclad guarantee of getting switch expressions—which is basically what I'm pitching here, and is very different from what I understand Ben to be insisting upon.

1 Like
(Preston Sumner) #67

I wanted to offer my experience: I conceptually understand what that code means, yet it's challenging for me to quickly parse at a glance, and I can't explain precisely why. I have to stop and read it carefully or else the tokens are mentally jumbled and indistinct. It could be related to issues of visual perception I experience that include afterimages and visual snow. Whatever the reason, I struggle to quickly translate it to its intended meaning, like struggling to fluently read sheet music despite knowing full well what the notes represent.

1 Like
(Matthew Johnson) #68

Well the emphasis there is on this part: “an indication of interest in pursuing that direction as a followup in the near term”. In other words I am willing to leave it up to the core team whether the two should be in the same proposal or not. But I will reiterate the portion of my comment that you edited out:

I think it would be pretty awkward to have one and not the other in the language for an extended period of time.

I think the core team should strive to avoid this situation. This means I think there should be an intent to use Apple (or Google) resources to purse switch expressions in the Swift 6 timeframe if if expressions are accepted on a standalone basis. Of course there is no ironclad guarantee - situations can change and unexpected things do happen. But I think there should at least be some intention to commit resources to avoiding the potentially awkward / confusing situation in the next major release.

1 Like
#70

One thing I would like to get clarified is if this will turn ALL if/else (with single expression inside) into expression, which may accidentally include ones meant to be statement, or is there more rule to this?

#71

Given that you can't assign or return the non-existent result of an if statement, does it matter? I can't imagine a code sequence where this change would change the behavior of existing code.

#72

For compiled behavior, no, it's source compatible.

If anything, I'm worried that the syntax can be either statement & expression, and will throw off the diagnostic (unused expression warning, for one), and may add more burden to the compiler (though how much, I do not know).

#73

I am not sure this is an issue. Consider

let x: Int
if condition() {
    x = 5
}
else {
    x = 10
}

The proposal would allow:

let x = if condition() { 5 } else { 10 }

If you wrote the above without the (currently illegal) assignment:

if condition() { 5 } else { 10 }

you would get warnings on both branches that the values are unused. If the proposal is implemented, this would not change. At worst, you would get an extra diagnostic about the if expression as a whole. In contrast, the first form, in which x is being assigned within the branches of the if, would create an expression which returns Void. Such expressions are already treated as if tagged with @discardableResult, and no new diagnostic would be issued.

1 Like
#74

Ok, that make senses, back to the review.

Still, I'm -1 on the proposal, feels like a properly indented ?: should get another chance.

#75

I believe I was the first to comment about properly-indented ternary expressions, but I still see value in if statements being expressions. Which of the following would you rather parse:

let x = condition1()
    ? 10
    : condition2()
        ? 9
        : condition3()
            ? 8
            : condition4()
                ? 7
                : ...

or

let x = if condition1() { 10 }
   else if condition2() { 9 }
   else if condition3() { 8 }
   else if condition3() { 7 }
   else if condition3() { 6 }
   ...

A properly-formatted nested ternary expression stair-steps. In the trivial example, it's not so bad, because I kept conditions and statements rather short. Having options allows us to write readable code and choose the correct syntax for the situation. Terse code is not always more readable.

(I realize that a switch-expression might be better than the if example, but let's keep the discussion to one construct at a time :slight_smile:)

#76

A fairer treatment would be

let longExpression = condition1() ? 10
    : condition2() ? 9
    : condition3() ? 8
    : condition3() ? 7
    : condition3() ? 6
    : 5

TBH, it'd rather parse neither. When expression gets this "complex", I'd start to refactor. There're many ways to break it down; local function, if-else statements, temp variable, etc.

Especially because the pitch aims at complex single statement, IMO if-else expression is the one that promotes terseness to the point that it starts to hurt.

4 Likes
#77

That's a fair point. What do you think about switch expressions? The conditions can include pattern matching and binding to associated enum values that are very awkward in regular condition expressions.

let amount = switch x {
    case 0: "none"
    case 1 ... 5: "a few"
    case 6 ... 10: "several"
    default: "lots"
}
7 Likes
(David Catmull) #78

My solution to the switch expression thing is to put the switch into a function. Such a switch is usually big enough that it doesn't seem like a stretch to give it its own function, and now I have a name for it too.

1 Like
#79

I don't see how that helps.

func amount(_ x: Int) -> String {
    switch x {
        case 0: return "none"
        case 1 ... 5: return "a few"
        case 6 ... 10: return "several"
        default: return "lots"
    }
}


func amount(_ x: Int) -> String {
    return switch x {
        case 0: "none"
        case 1 ... 5: "a few"
        case 6 ... 10: "several"
        default: "lots"
    }
}

The point of the pitch is to be able to write the latter, whether it's a separate function, or inline, like my previous comment. (I know that the pitch is about if expressions, but I digressed.)

(Dave Abrahams) #80

By exactly the same logic, a properly formatted if/else chain stairsteps too, doesn't it?

2 Likes
#81

As Lantua showed, Xcode does allow for a non-stairstepping format for ternary expressions, so my original statement was not really correct. In any event, I've never seen Xcode insist that a series of if-else-if statements be indented in a stair-step manner, and I can't conceive a reason for anyone to do so manually.