# Pitch: Multi-statement if/switch/do expressions

**URL:** <https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443>\
**Category:** Pitches\
**Created:** [November 14, 2023, 1:07am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443 "2023-11-14T01:07:11Z")\
**Posts on this page:** 20\
**Page:** 15

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [November 28, 2023, 3:27am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/284 "2023-11-28T03:27:43Z")

</div>

> [@Jon\_Shier](#):
>
> Sorry, but I'm completely uninterested in discussing examples specifically contrived to trick users into misunderstanding their structure.

I think @tera’s argument would be far more convincing if it used SwiftUI’s `ForEach` or another function that accepts a trailing closure.

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [November 28, 2023, 3:36am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/285 "2023-11-28T03:36:08Z")

</div>

`ForEach` uses a result builder, so there's all sorts of weird stuff going on. And I'm not sure what you mean about a trailing closure, `return` within a closure return a value to the closure. Constructing in such a way that you try to return through it just doesn't work. Only bare statements can appear to return through themselves. So I guess the question is, are if / switch expressions more like functions / closures or more like their statement counterparts? I'd argue that, by giving them the current implicit return behavior, they're much more like functions and closures than statements, and so can be treated as such more fully to solve the problem outlined in the pitch.

---

<div class="post-metadata">

**Author:** ![sspringer](https://avatars.discourse-cdn.com/v4/letter/s/e9c0ed/32.png) [@sspringer](https://forums.swift.org/u/sspringer)\
**Post date:** [November 28, 2023, 3:52am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/286 "2023-11-28T03:52:31Z")

</div>

> [@Jon\_Shier](#):
>
> are if / switch expressions more like functions / closures or more like their statement counterparts

The second. At least not the first.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [November 28, 2023, 5:25am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/287 "2023-11-28T05:25:39Z")

</div>

> [@Jon\_Shier](#):
>
> So I guess the question is, are if / switch expressions more like functions / closures or more like their statement counterparts?

Obviously if/switch expressions are more like statements than functions/closures:

- no "in"
- no closure / capture list
- can't assign the "uncalled form" to a variable, pass that variable around and call that variable later on
- can't have a return (at least in a "single statement" if expression) while there is no problem of having "return" in a single statement function/closure.)
- no "non trailing closure" form:

```swift
    let x = if (true, then: {1}) // 🛑
    let x = if (true, then: {1}, else: {2}) // 🛑

```

> [@Jon\_Shier](#):
>
> But returning _through_ expressions is exactly what was initially proposed in SE-380 and was discarded precisely because it _is_ confusing

Thanks for the reminder! Reviewing that thread I found that using "return" to mean "expression value" was also regarded as confusing (see discussion for Option #1 linked below).

[This](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899/45), [this](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899/77) and [this](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899/90) post are particularly interesting. As a compromise solution I like (Option #5), makes sense (bare last expression in if/switch statements, explicit return in functions/closures).

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [November 28, 2023, 5:41am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/288 "2023-11-28T05:41:11Z")

</div>

BTW, I found that "try" marker is not required (and not allowed) for an if expression. Is that a bug or a feature?

```swift
func foo() throws {
    let x = if condition { throw NSError() } else { 42 } // ✅
    let y = try if condition { throw NSError() } else { 42 } // 🛑 'try' may not be used on 'if' expression
}

```

It looks like a feature due to the error description but OTOH it feels like a bug at the first and the second glance.

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [November 28, 2023, 7:29am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/290 "2023-11-28T07:29:19Z")

</div>

> [@tera](#):
>
> Is that a bug or a feature?

If the question is whether it's by design, then the answer is yes, it was designed that way.

---

<div class="post-metadata">

**Author:** ![jpmhouston](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jpmhouston/32/4045_2.png) [@jpmhouston](https://forums.swift.org/u/jpmhouston)\
**Post date:** [November 28, 2023, 8:29am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/291 "2023-11-28T08:29:41Z")

</div>

> [@tera](#):
>
> Example of the current hard to read code:
> 
> ...
> 
> (I deliberately used a funny "iƒ" function name to make it harder to read, but even if it was, say, "when" or "iff" or "on" sometimes it is easy to miss the fact that the following block is a closure, not a statement block).

I'd argue than _any_ closure makes this puzzling to read, not just one disguising itself as a statement or alluding as such, and that this has always been an issue for Swift:

```swift
func foo() -> Int {
    let x = 42
    banana(x) { // a closure!!!
        print("1")
        return 1 // does NOT return from "foo"
    }
    if x == 42 {
        print("2")
        return 2 // returns from "foo"
    }
}

```

Why are we required to use the same syntax in one vs. another when they clearly produce different results? [edit addition for clarity:] The one vs. other I mean are the closure denoted by braces vs. the control flow clause denoted by braces. A new keyword that's available for use within closures will naturally also solve the issue with `if`/`switch` expressions being discussed here.

In this thread the placeholder suggestion for this keyword has been spelled `use`, can this be spelled `yield` instead? Would it cause confusion with other (still proposed?) use of `yield`? 😕

---

<div class="post-metadata">

**Author:** ![jaleel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jaleel/32/45076_2.png) [@jaleel](https://forums.swift.org/u/jaleel)\
**Post date:** [November 28, 2023, 8:59am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/292 "2023-11-28T08:59:35Z")

</div>

> [@jpmhouston](#):
>
> Why are we required to use the same syntax in one vs. another when they clearly produce different results?

But they are the same in a sense that those are two closures basically, one just have `if {}`, I'm not sure about `clearly`...

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 28, 2023, 4:11pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/293 "2023-11-28T16:11:04Z")

</div>

> [@jpmhouston](#):
>
> Why are we required to use the same syntax in one vs. another when they clearly produce different results?

I wonder if there's any merit in permitting a more verbose but explicit form of return, and if that might sway the decision here (for if/switch/do expressions)? e.g.:

```swift
func foo() -> Int {
    let x = 42
    banana(x) { // a closure!!!
        print("1")
        return 1 from foo // 🛑 Cannot return from foo from inside a nested closure.
    }
    if x == 42 {
        print("2")
        return 2 from foo // ✅
    }
}

```

(Or `return from foo with 1` or `return foo: 1` or `return foo = 1` or whatever syntax you prefer)

There's also the option to make this _required_ whenever there's any potential ambiguity (e.g. when inside a nested function or closure), which itself could be controlled through a compiler flag (in the same vein as `SWIFT_STRICT_CONCURRENCY` e.g. `STRICT_CONTROL_FLOW`).

I'm not sure how you'd name an anonymous function (a la closures), though. Maybe something to do with the variable or function they're invoked via, e.g. `return 1 to banana` in the above example? Semantically a bit incongruent, though.

---

<div class="post-metadata">

**Author:** ![Pippin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pippin/32/24826_2.png) [@Pippin](https://forums.swift.org/u/Pippin)\
**Post date:** [November 28, 2023, 4:26pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/294 "2023-11-28T16:26:02Z")

</div>

> [@wadetregaskis](#):
>
> return foo: 1

This is what I thought about with return labels, a function's return label is the function name and a variables return label is the variable name. This mirrors how labels already work with control statements.

```swift
func foo() -> Int {
    var a: Int = if bar {
           // label explicit for example,
           // where `return 1` would return 1 to `a`
           return a: 1
        } else {
            // reduced example
            return foo: 2
        }

    // do stuff with `a`
}

```

> [@wadetregaskis](#):
>
> I'm not sure how you'd name an anonymous function (a la closures), though

This would actually work just but only the _first_ trailing closure _cannot_ have a label, but this [seems unpopular](https://forums.swift.org/t/accepted-se-0279-multiple-trailing-closures/36141). So I could see the introduction of that behavior/syntax leading to first trailing closure labels - therefore filling in another perceived gap of the language and ushering in the return of a desired feature.

```swift
// current
foo(...) {
    // n/a
} bar: { // assume type: () -> Int
    let a: Int = if {
        // label explicit for example
        return a: 1
    } else {
        // reduced example
        return bar: 2
    }

    // do stuff with `a`
}

// future
foo(...) bar: { // assume type: () -> Int
    let a: Int = if {
        // label explicit for example
        return a: 1
    } else {
        // reduced example
        return bar: 2
    }

    // do stuff with `a`
} { ... } // assume I have any number of trailing closures

```

Edit: another example after my morning coffee

```swift
// bar is the label for the previously anonymous closure
let foo: () -> Int = bar: {
    let a: Int = if {
        return a: 1
    } else {
        return bar: 2
    }
}()

```

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 29, 2023, 3:45am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/295 "2023-11-29T03:45:09Z")

</div>

One downside of this is that it couples the contents of the closure (specifically the return 'labels') to the function parameter names. And not in a useful way, as far as I can tell - it shouldn't matter whether it's "bar:" or "troz:" or "zort:" as far as the closure is concerned.

It'd be a relatively minor concern _if_ shadowing weren't a potential issue. e.g. the function parameter "bar:" might be renamed "troz:" and it turns out there's _another_ "bar:" further up, so the closure is now trying to return to a _different_ place than intended. It _might_ get caught by various other constraints, but it seems potentially error-prone (for compiler authors) and fragile.

---

<div class="post-metadata">

**Author:** ![Pippin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pippin/32/24826_2.png) [@Pippin](https://forums.swift.org/u/Pippin)\
**Post date:** [November 29, 2023, 5:08am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/296 "2023-11-29T05:08:35Z")

</div>

Frankly, if you're using this feature and you want to return through it, you're explicitly opting into that pattern. Wanting to return a function value within the evaluation syntax of an expression is a huge code smell, but may be desirable in a small chunk of manageable code and we surely won't stop you from doing it.

> [@wadetregaskis](#):
>
> it turns out there's _another_ "bar:" further up

I was also assuming the current rules for labeled statements would apply in that there cannot be duplicate nested labels - the safety is enforced by the compiler.

Frankly the situation where there may be duplicate function/control statement labels only means that you need to simplify and re-organize your code.

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 29, 2023, 5:43am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/297 "2023-11-29T05:43:23Z")

</div>

> [@Pippin](#):
>
> Frankly the situation where there may be duplicate function/control statement labels only means that you need to simplify and re-organize your code.

But in this situation it might not _be_ your code - it's controlled by the functions you're calling; they name their parameters.

---

<div class="post-metadata">

**Author:** ![Pippin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pippin/32/24826_2.png) [@Pippin](https://forums.swift.org/u/Pippin)\
**Post date:** [November 29, 2023, 6:49am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/298 "2023-11-29T06:49:38Z")

</div>

How is that practically different than when function parameters change today? That conversation can now be shifted to library deprecations and client maintenance responsibilities.

---

<div class="post-metadata">

**Author:** ![jaleel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jaleel/32/45076_2.png) [@jaleel](https://forums.swift.org/u/jaleel)\
**Post date:** [November 29, 2023, 10:00am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/299 "2023-11-29T10:00:43Z")

</div>

Looks like goto to be honest, think functions/closures should have more deterministic behaviour with return (which they have now).

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 29, 2023, 4:43pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/300 "2023-11-29T16:43:33Z")

</div>

> [@Pippin](#):
>
> How is that practically different than when function parameters change today? That conversation can now be shifted to library deprecations and client maintenance responsibilities.

The difference is you'll get a compiler error if a parameter name changes, forcing you to modify _that part_ of your code, but you won't necessarily get _any_ indication from the compiler that some `return` statement, an unbounded number of lines further down the file, has now unintentionally changed targets.

> [@jaleel](#):
>
> Looks like goto to be honest, think functions/closures should have more deterministic behaviour with return (which they have now).

That seems contrary. There's not much _more_ deterministic than `goto` - it's _explicitly_ saying exactly where control flow is going. Unlike `return` which - as this centithread has stressed - is unclear and ambiguous about what it's doing, if looked at in local isolation. An emphasis on _local reasoning_ is one of the better aspects of Swift.

There's a prevalent anti-`goto` cargo cult in programming, reinforced by occasional [goto fail](https://dwheeler.com/essays/apple-goto-fail.html) incidents (which are really more about fundamentally, horribly broken coding practices, orthogonal to `goto`). I think that's unhelpful. I'm also not advocating for actual `goto` - everything being discussed here is for more restricted forms of that basic notion, that avoid the genuine pitfalls. e.g. `return to <label>` / `break label` / `continue label` each have very specific control flow semantics with a _limited_ set of label positions they can be used with, making it easier to reason about their behaviour - and ensure _intended_ behaviour.

---

<div class="post-metadata">

**Author:** ![Pippin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pippin/32/24826_2.png) [@Pippin](https://forums.swift.org/u/Pippin)\
**Post date:** [November 29, 2023, 5:28pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/301 "2023-11-29T17:28:55Z")

</div>

> [@wadetregaskis](#):
>
> The difference is you'll get a compiler error if a parameter name changes, forcing you to modify _that part_ of your code, but you won't necessarily get _any_ indication from the compiler that some `return` statement, an unbounded number of lines further down the file, has now unintentionally changed targets.

Current label rules already enforce label existence and since duplicates are already not allowed, this is a non issue. I fail to see where at any point a return point “unintentionally changed targets” - at any point where a parameter changes either by your code or others, it will either cause a conflict with your own labels or will fail because the old label no longer exists. We get that compiler safety at all times.

I thought this would have been implicit, but you would also never be able to return outside of your closure/function. _That’s_ the rule that would further enforce determinism and would make local reasoning about your return labels more … reasonable.

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [November 29, 2023, 9:01pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/302 "2023-11-29T21:01:30Z")

</div>

> [@Pippin](#):
>
> _That’s_ the rule that would further enforce determinism and would make local reasoning about your return labels more … reasonable.

Indeed I was thinking about a more generalised approach, because otherwise - with your proposed restriction - what's the benefit of this over just using `then`?

---

<div class="post-metadata">

**Author:** ![jaleel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jaleel/32/45076_2.png) [@jaleel](https://forums.swift.org/u/jaleel)\
**Post date:** [November 29, 2023, 9:23pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/303 "2023-11-29T21:23:22Z")

</div>

> [@wadetregaskis](#):
>
> There's a prevalent anti-`goto` cargo cult in programming

That's why I've wrote `looks like`, of course it's not.  
And not sure there is some cargo cult, debate is [quite old](https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf).

> [@wadetregaskis](#):
>
> Unlike `return` which - as this centithread has stressed - is unclear and ambiguous about what it's doing, if looked at in local isolation.

It's just example is small, if you extend it a bit then good luck supporting this code 🙃

```swift
func foo() -> Int {
   let a = switch bar {
     case "1": return a: 1
     case "2": return foo: 0
     case "3": 
       print("Weird")
       return foo: 0
     case "4":
       return a: 5
     case "5": return a: 1
     case "6": return foo: 0
     case "7": 
       print("Weird")
       return foo: 0
     case "8":
       return a: 5
     case "9": return a: 1
     case "10": return foo: 0
     case "11": 
       print("Weird")
       return foo: 0
     case "12":
       return a: 5
     default:
       return foo: -1
   }

    // do stuff with `a`
}

```

---

<div class="post-metadata">

**Author:** ![Pippin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/pippin/32/24826_2.png) [@Pippin](https://forums.swift.org/u/Pippin)\
**Post date:** [November 29, 2023, 9:51pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/304 "2023-11-29T21:51:32Z")

</div>

> [@wadetregaskis](#):
>
> what's the benefit of this over just using `then`?

And now we come full circle. I believe there are enough comments in this thread to answer that question.

> [@jaleel](#):
>
> It's just example is small, if you extend it a bit then good luck supporting this code 🙃

I find that no different than a contrived confusing example with existing control flow labels - and I have left enough comments why something like that is a code smell.

> [@jaleel](#):
>
> That's why I've wrote `looks like`

I will agree that one may think it looks like it, but the `return label: value` translates to "return a value to this label" and I find that pretty straightforward. Of course that would be explained in something like the Swift Book, but I find it intuitive enough to figure out on one's own.

[Previous page](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443.md?page=14)

[Next page](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443.md?page=16)
