If / else expressions

I'd argue they both have their place. I do think the if/else can be more readable, as in the examples given in this thread.

On the other hand, one of the places I'm most likely to use a ternary operator is inside a string interpolation. Adding a variable beforehand clutters up the code with things to think about that aren't relevant to the operation of the main flow, and at least in my opinion having an if/else or switch expression inside one of those would be insane to read.

I agree, but I'm strongly in favor of this :smiley:

I also think that the core team should redact the already posted commonly rejected changes on control flow to remove the point about if/else and switch as expressions: it's easy to show examples of the convenience that this change would bring, and whoever tries to keep its code referentially transparent (which is a very good idea for code understandability, in general) will benefit greatly from this.

1 Like

I'd really love to rewrite

switch sets.count {
    case 0: cell.detailTextLabel?.text = ""
    case 1: cell.detailTextLabel?.text = sets[0].name
    default: cell.detailTextLabel?.text =
        String(format: NSLocalizedString("(%d selected)", comment: ""), sets.count)
    }

as

cell.detailTextLabel?.text = 
    switch sets.count {
        case 0: ""
        case 1: sets[0].name
        default: String(format: NSLocalizedString("(%d selected)", comment: ""), sets.count)
        }
13 Likes

That would be great, and shows another important improvement of switch-as-expression: in your version, the compiler would enforce returning a String from all the cases, including the default.

4 Likes

I love the ternary operator for appropriate one liners, but I would never nest them.

In Ruby recently I've been pointed out that I can also use the if/then syntax on one line:

result = if cond then "Foo" else "Bar" end

which is readable I guess, but seems similar to this pitch. Ruby uses the value of the last line as a return value. I'm not a language or compiler expert, so I don't know what kind of uncertainty this creates.

However, rather than creating a new special way to use if/switch only under restricted conditions, I'm truly curious to hear what people think about crafting Swift to allow the value of the last line to be optionally used as a return value for these conditional constructs (when preceded by an assignment operator)!

let result = if cond then {
    1
}
else {
    if cond2 {
        2
    }
    else {
        switch cond3 {
        case "a": 3
        case "b": 4
        default: 5
        }
    }
}

let result = switch flag {
case .one: 1
case .two: 
    // some line
    // another line
    2
}

Evaluating the last reached line throughout the conditions seems flexible and consistent to me. I think we could be more explicit with a keyword like assign if necessary because return within a function has other purpose, right? (Although this feels a bit verbose and too much of a special case)

let result = switch flag {
case .one: assign 1
case .two:
    if cond {
        assign 2
    }
    else {
        assign 3
    }
}

So this would be when an assignment operator precedes a conditional? How would we handle optionals?

let result = switch flag {
case .one: 1
case .two: nil
} ?? 3

Anyway, just curious to hear opinions!

Just to pitch in with a quick snippet that I'm literally working on right now (but I write code like this all the time).

Imagine if this:

let errorLabelIsVisible: Bool

switch error {
case .canceled:
    errorLabelIsVisible = false
case .invalidResponse(let response) where response.statusCode == 404:
    errorLabelIsVisible = false
default:
    errorLabelIsVisible = true
}

/// use errorLabelIsVisible

could be written as this:

let errorLabelIsVisible = switch error {
case .canceled: false
case .invalidResponse(let response) where response.statusCode == 404: false
default: true
}
/// use errorLabelIsVisible

It would be glorious for Swift, but many languages feature this, for example Kotlin (without the where).

9 Likes

The acceptance of SE-0255: Implicit Returns from Single-Expression Functions - #113 by Ben_Cohen recognizes that a natural next step might be if and switch expressions. I would really like to pursue this direction. Is anyone else interested in reviving this topic in light of that decision?

14 Likes

Extremely interested and strongly +1 in favour of both if/else and switch expressions.

2 Likes

That's really great news about the single line implicit returns there :slight_smile:
I'd be very much interested in seeing if/switch move it to the next level of usefulness :heart:
Sounds like a good time to put into place a longer writeup, gathering the feedback from this thread perhaps?

2 Likes

To be slightly pedantic, because this wording was deliberate: the review acceptance acknowledged "that accepting this feature may encourage follow-on proposals that generalize it further." Describing it as a natural next step implies endorsement, whereas we wanted to be clear we weren't endorsing or discouraging at this stage.

12 Likes

Have you been spending time with the legal team recently? It shows :stuck_out_tongue:
Reminds me of The Lawyer's Hoilday card

Please accept with no obligation, implied or implicit, our best wishes for an environmentally conscious, socially responsible, low stress, non-addictive, gender neutral celebration of the winter solstice holiday, practiced with the most enjoyable traditions of religious persuasion or secular practices of your choice with respect for the religious/secular persuasions and/or traditions of others, or their choice not to practice religious or secular traditions at all. We also wish you a fiscally successful, personally fulfilling and medically uncomplicated recognition of the onset of the generally accepted calendar year 2007, but not without due respect for the calendars of choice of other cultures whose contributions to society have helped make our country great (not to imply that the United States is necessarily greater than any other country) and without regard to the race, creed, color, age, physical ability, religious faith or sexual preference of the wishee.

By accepting this greeting, you are accepting these terms: This greeting is subject to clarification or withdrawal. It is freely transferable with no alteration to the original greeting. It implies no promise by the wisher to actually implement any of the wishes for her/himself or others and is void where prohibited by law, and is revocable at the sole discretion of the wisher. This wish is warranted to perform as expected within the usual application of good tidings for a period of one year or until the issuance of a subsequent holiday greeting, whichever comes first, and warranty is limited to replacement of this wish or issuance of a new wish at the sole discretion of the wisher.

Disclaimer: No trees were harmed in the sending of this message; however, a significant number of electrons were slightly inconvenienced

On a more serious note, I'm glad SE-0255 got passed, which make these proposed if/else/switch expressions feel much more "at home".

5 Likes

Sorry, I didn’t mean to imply you endorsed it, that was very clear. It seems like the core team acknowledged this as a natural next step for discussion that some people would inevitably want to see happen. I just wanted to get the discussion going again since I am personally very much in favor of this direction. Of course it will be subject to the same process as everything else and acceptance is not inevitable. :slight_smile:

3 Likes

I won't be able to aid in the design, but I am very interested in endorsing and cheer-leading this effort :-)

edit: More specifically, when the return elision lands I have ~50 declarations to update in String's implementation. When I do that, I'll report back how many are foiled by simple control flow. In practice, I expect most of the remaining are foiled by assertions, as I am a firm believer in the assert-liberally approach to software.

10 Likes

I'm very interested. Here's something that I posted in the SE-0255 thread that is relevant here:

3 Likes

I'm interested in this one as well. I would use both if/else and switches. Swift's switch pattern matching together with its range syntax are a great fit for numeric programming.

I worry when I write assignment to a variable more than once that I will somehow lose track of the separate occurrences. I know that with let semantics, and with the compiler checking that a variable is initialized before use, that I shouldn't worry... but somehow the first of these two code blocks feels more likely to stay correct over time:

// if c is outside interval or too close to edges move it towards the middle
let c1: Double = switch (b - a,c) {
    case (...(4 * δ),_): (a + b) / 2
    case (_,...(a + 2 * δ)): a + 2 * δ
    case (_,(b - 2 * δ)...): b - 2 * δ
    default: c
    }

let c2: Double
switch (b - a,c) {
    case (...(4 * δ),_): c2 = (a + b) / 2
    case (_,...(a + 2 * δ)): c2 = a + 2 * δ
    case (_,(b - 2 * δ)...): c2 = b - 2 * δ
    default: c2 = c
}

I'm also willing to entertain the idea that I just need to update my attitude and that the second code block is just fine. I suspect I have baggage from less safe languages affecting me here.

3 Likes

The reason it's weird is that there are no words in the ternary expression. There are many symbols that map directly to words (>, <, != and many more). When reading those symbols we convert them to words in our head. What words do "?" and ":" convert to? I think code should be readable. You should be able to read it out loud and understand it. Nested ternary expressions don't work like that for me.

One of my rules of thumb is to never use nested ternary expressions. They're a kind of one way coding. You can write them but then not understand them the next day. I would almost certainly use a compound if statement to express the kinds of things expressed in this thread as nested ternary expressions and I'd live without the terseness.

Regarding the pitch I kind of like it in some cases where there's an explicit assignment. The cases that use an implicit assignment, return statement, inside print(), may require rereading to realize that this is not a normal if statement but an if expression. And that may be just as confusing as a nested ternary expression.

3 Likes

What about:

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

The comment you replied to was written before one was allowed to omit return from a single-statement function. Now, of course, that would be acceptable if switch became an expression.

1 Like

Now when function builders can handle switch statements and if else, it feels like we basically have to turn them into expressions.

I mean, isn't it already possible to hack something together that looks like that today, using function builders? It should definitely be possible for individual types, but perhaps not in a generic way.

1 Like

I agree. With the combined precedent of function builders turning control flow into expression-ish things and implicit returns for single-expression functions etc, we should strongly reconsider turning if and switch at least into expressions.

I recently worked on a Rust project for a bit, and the if and match expressions are really handy to use.

2 Likes