# 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:** 17\
**Page:** 24

<div class="post-metadata">

**Author:** ![Jeehut](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jeehut/32/21743_2.png) [@Jeehut](https://forums.swift.org/u/Jeehut)\
**Post date:** [October 16, 2024, 3:04pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/473 "2024-10-16T15:04:27Z")

</div>

If I understand this pitch correctly, it would allow me avoid writing this code:

```swift
let data: Data
let response: URLResponse
do {
   (data, response) = try await URLSession.shared.data(for: request)
} catch {
   throw RequestError.failedToLoadData(error)
}

```

And instead I could write this much more readable and convenient code:

```swift
let (data, response) = do {
   try await URLSession.shared.data(for: request)
} catch {
   throw RequestError.failedToLoadData(error)
}

```

Do I understand that right?

For this aspect alone, I'm supporting this pitch. I'm not so sure about the introduction of a `then` keyword though, I don't find it very intuitive on first read of the proposal. The aspect I'm most interested in is the allowance of do-expressions. It might be added in a much narrower proposal. I pitched another change here to solve the same problem:

> [@\[Pitch\] Introducing do-let-catch for Cleaner Error Handling](https://forums.swift.org/t/pitch-introducing-do-let-catch-for-cleaner-error-handling/75348):
>
> I recently encountered a scenario where I found the existing syntax in Swift unnecessarily complex and less readable: let data: Data let response: URLResponse do { (data, response) = try await URLSession.shared.data(for: request) } catch { throw RequestError.failedToLoadData(error) } I believe it would be far more readable and concise if we could write it this way: do let (data, response) = try await URLSession.shared.data(for: request) catch { throw RequestError.failedToLoadData(err…

---

<div class="post-metadata">

**Author:** ![7ombie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/7ombie/32/36317_2.png) [@7ombie](https://forums.swift.org/u/7ombie)\
**Post date:** [December 14, 2024, 6:54pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/474 "2024-12-14T18:54:15Z")

</div>

This isn't the pitch for do-expressions. This thread's exploring an extension that would allow the blocks to contain more than a single expression.

---

<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:** [December 14, 2024, 11:29pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/475 "2024-12-14T23:29:06Z")

</div>

> [@7ombie](#):
>
> This isn't the pitch for do-expressions.

This absolutely is the pitch for `do` expressions:

> [@](#):
>
> ## Detailed Design
> 
> [...]
> 
> Additionally `do` statements will become expressions

---

<div class="post-metadata">

**Author:** ![Jeehut](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jeehut/32/21743_2.png) [@Jeehut](https://forums.swift.org/u/Jeehut)\
**Post date:** [December 16, 2024, 10:50am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/476 "2024-12-16T10:50:42Z")

</div>

I do understand the confusion. I wish there was a proposal that was more narrowed down to do-expressions. I was confused, too, see [here](https://forums.swift.org/t/pitch-introducing-do-let-catch-for-cleaner-error-handling/75348/4). `do` expression only seem to be a side aspect.

---

<div class="post-metadata">

**Author:** ![georgeef](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/georgeef/32/38378_2.png) [@georgeef](https://forums.swift.org/u/georgeef)\
**Post date:** [December 23, 2024, 10:06pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/477 "2024-12-23T22:06:51Z")

</div>

To me this proposal adds little value with a weird syntax. Its main problem is that the new keyword `then` doesn't read naturally. It looks like a linguistic syntax error, especially when `then` is interweaved with `else`.

Below I propose an enhanced syntax, which is a bit more general and easier to read. I use special symbols which may conflict with the parser. Please consider mostly the introduced features, not the exact symbols.

The main purpose of the new "branch expression" (BE) syntax is to define a "branch value" without repeating the lvalue or an auxiliary constant/variable name. In this respect, a BE looks more like a functional expression, since it tries to anonymize the intermediate variables.

The following code is very similar to the example in the original proposal:

```swift
switch scalar.value {
  case 0..<0x80: longLValue = 1
  case 0x80..<0x0800: longLValue = 2
  case 0x0800..<0x1_0000: longLValue = 3
  default: 
    log("this is unexpected, investigate this")
    longLValue = 4
}

```

The purpose is to avoid the repetition of `longLValue`, which might be a long string containing subscripts, and also to avoid introducing a new intermediate constant/variable. The abbreviated code shall look as a single assignment, with one composite expression as rvalue.

As already commented, replacing `longLValue =` with `return` is a NO-GO, but it worths mentioning a few similarities. If the above code was the body of a closure it would look like:

```swift
switch scalar.value {
  case 0..<0x80: return 1
  case 0x80..<0x0800: return 2
  case 0x0800..<0x1_0000: return 3
  default: 
    log("this is unexpected, investigate this")
    return 4
}

```

The return value is anonymous. Once we hit a `return`, we set the return value and we skip the rest of the code. The `return` keyword has the meaning "set and done". (In a pure function, it also has the equivalent but more relaxed meaning: "set the return value and you may skip the rest; you can continue if you want, but nothing is going to change, since the return value is already set and it cannot be overwritten or unset, so all subsequent `return` are not effective".)

The above "set and done" semantics are exactly what is needed in place of `longLValue =` in the original code, or in a more complex BE body.

Principles of the BE syntax:

- A BE is introduced with the special symbol `.=`. This makes it clear that the rvalue has special semantics for the inner `.=` symbols.
- The lvalue of the wanted value and the assignent operator are replaced with `.=`, which can be spelled as "set and done".
- Effectively the lvalue is pulled before the BE and it is factored out.
- The BE is a single `if`/`switch`/`do`/`for`/`while` expression, but its inner body may be complex (as complex as the body of a closure).
- All BE expressions (not only `if`) may have an `else {}` part. The `else` body is executed in case of a miss (no `.=` is executed in the main body).

The above example would be abbreviated as:

```swift
longLValue .= switch scalar.value {
  case 0..<0x80: .= 1
  case 0x80..<0x0800: .= 2
  case 0x0800..<0x1_0000: .= 3
  default:
    log("this is unexpected, investigate this")
    .= 4
}

```

As it is common in Swift, `.=` may be omitted if it can be inferred (as in the first 3 cases above).

Visually the `.=` symbol acts like a continuation marker. The outer `.=` continues to the applicable inner `.=`, depending on the conditions. Writing the code is as easy as thinking of the quivalent closure and replacing `return` with `.=`. When reading the full syntax, `.=` is interpretted as "set and done". Reading the simplified code after reducing `.=`, is more challenging (and often more natural), as with other simplifications in Swift.

Another example:

```swift
let first: Int? .= for i in 0..<a.count {
  if found(a[i]) { .= i }
} else {
  .= nil
}

```

An interesting extension is that an optional lvalue can take its default `nil` value in case of a BE miss, therefore the `else` part above can be omitted.

The "set and done" semantics is a good fit to "first-of" type of algorithms, e.g., finding the first index, as in the example above. It is not such a good fit to "full-scan" algorithms which need to update a state, e.g., finding the max value.

Another interesting extension is nested BE's. I don't think that this feature worths the effort, and I don't recommend it, but it worths considering it as an exercise. The inner body of a BE can be any body, which could contain another (nested) BE. We can use the symbol `..=` for the inner anonymous value, which allows full flexibility on how the two anonymous variables are set. This flexibility introduces some new challenges, e.g., which part of the code to skip if the inner value is set first. (Another approach is to restrict the syntax like in nested closures, where each `return` has a well-defined context.)

Regarding the inner `.=` symbol: of course it can be replaced with another symbol or another keyword. I would prefer to have a symbol which allows an extension to nested BE's (just as a possibility, even if it is never implemented).

Regarding the outer `.=` symbol: maybe it can be replaced by `=`, if the compiler can recognize that the rvalue is a BE. In general I don't like this approach. The rvalue is something different than what we had before. Using the same syntax for the assignment just hides this information. We pretend that nothing changed, although there are new semantics. This approach quite often fires back, since it over-simplifies syntax by adding confusion (and by the way this is a favorable approach in Swift).

---

<div class="post-metadata">

**Author:** ![georgeef](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/georgeef/32/38378_2.png) [@georgeef](https://forums.swift.org/u/georgeef)\
**Post date:** [December 25, 2024, 1:47pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/478 "2024-12-25T13:47:40Z")

</div>

A more conventional syntax:

- Replace the outer `.=` with `=`
- Replace the inner `.=` with `bind`

BE can be pronounced "binding expression".

The second example above becomes:

```swift
let first: Int? = for i in 0..<a.count {
  if found(a[i]) { bind i }
}

```

---

<div class="post-metadata">

**Author:** ![7ombie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/7ombie/32/36317_2.png) [@7ombie](https://forums.swift.org/u/7ombie)\
**Post date:** [January 20, 2025, 4:07pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/479 "2025-01-20T16:07:38Z")

</div>

> [@georgeef](#):
>
> `return` is a NO-GO

Why though? It seems to have been rejected because it was assumed to conflict with regular use of `return`, but that's not actually the case in practice.

If the grammar `do <statement>` wraps the statement in a closure and invokes it, then any `return` within the statement is just returning from the implied closure. It doesn't change the semantics of `return`.

You never encounter a situation where you need a branch or case to return (from the containing function) _and_ evaluate to something locally (within the function), as returning (anything) precludes evaluating to anything locally.

```swift
let foo = if predicate {
    return // return from function (`foo` is never initialized)
} else {
    do { // this invocation is a single, boolean expression
        if x { log("x") } else { log("y") }
        return true
    }
}

```

---

<div class="post-metadata">

**Author:** ![georgeef](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/georgeef/32/38378_2.png) [@georgeef](https://forums.swift.org/u/georgeef)\
**Post date:** [January 20, 2025, 4:27pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/480 "2025-01-20T16:27:51Z")

</div>

From syntax point of view it should be ok, and it won't be difficult for the compiler to parse it (maybe I am wrong), but for human readers it will be very confusing. An in-place closure also contains `return`, but over the years human readers developed the ability to easily recognize the closure pattern. A binding expression is a new pattern, and maybe no more difficult for new programmers to accept it, but for experienced programmers it is more prone to confusion compared to in-place closure. Essentially it is an unnecessary re-learning process for how to quickly recognize the meaning of `return`.

Anyway this Pitch is history.

---

<div class="post-metadata">

**Author:** ![7ombie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/7ombie/32/36317_2.png) [@7ombie](https://forums.swift.org/u/7ombie)\
**Post date:** [January 20, 2025, 4:44pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/481 "2025-01-20T16:44:18Z")

</div>

Using `do` to introduce IIFEs has been done before, and it's very simple grammar: `do <block>` is equivalent to `<block>()`.

> Anyway this Pitch is history.

Fair enough. Using `do` as an IIFE prefix has other usecases, but they're always hard to articulate, because it's useful in lots of little ways.

---

<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:** [January 21, 2025, 7:29am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/482 "2025-01-21T07:29:32Z")

</div>

> [@7ombie](#):
>
> If the grammar `do <statement>` wraps the statement in a closure and invokes it…

A “do” section is _not_ a closure, it just hides variables, is used for error handling, and with this pitch it would be used _instead_ of a closure to calculate a result.

---

<div class="post-metadata">

**Author:** ![7ombie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/7ombie/32/36317_2.png) [@7ombie](https://forums.swift.org/u/7ombie)\
**Post date:** [January 22, 2025, 2:56am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/483 "2025-01-22T02:56:29Z")

</div>

That's why I began with _if_.

---

<div class="post-metadata">

**Author:** ![NSExceptional](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nsexceptional/32/6813_2.png) [@NSExceptional](https://forums.swift.org/u/NSExceptional)\
**Post date:** [April 28, 2025, 8:01pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/484 "2025-04-28T20:01:00Z")

</div>

Can someone summarize what the consensus was for this pitch? Seems like a great idea to me, and the PR is still open, but it never went anywhere?

(Sorry, I cannot sift through 500 replies, hoping someone who participated in the discussion can comment)

---

<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:** [April 28, 2025, 9:08pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/485 "2025-04-28T21:08:47Z")

</div>

Summarizing is a task to which LLMs are uniquely well suited, I think. [Here's one such summary.](https://www.perplexity.ai/search/please-summarize-what-the-cons-R6kM06Q9T3qg4aGmqiA9eQ)

---

<div class="post-metadata">

**Author:** ![NSExceptional](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nsexceptional/32/6813_2.png) [@NSExceptional](https://forums.swift.org/u/NSExceptional)\
**Post date:** [April 28, 2025, 9:31pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/486 "2025-04-28T21:31:28Z")

</div>

I have not had such luck with LLMs in this area—i.e. I tried to get ChatGPT to summarize a PubMed study and it kept hallucinating which study I was linking it. For some reason whatever logic they use to extract URLs from input was either not working or not being invoked at all.

Anyway, while the thought had crossed my mind, I opted not to try it on this page, since there are 500 paginated replies, and I doubt it can very well read and summarize all replies in a timely manner, since they cannot all be loaded at once. And for that reason I am skeptical. Even the AI is skeptical:

> _It's worth noting that these conclusions are based on the available search results, which may not capture the complete discussion or final consensus._

Even reading the summary, I'm skeptical of its accuracy. It feels hallucinated, or like it was only based off of a few replies. The summary also doesn't address why this pitch hasn't moved forward, which is my real question.

---

<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:** [April 28, 2025, 10:18pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/487 "2025-04-28T22:18:57Z")

</div>

> [@NSExceptional](#):
>
> Even reading the summary, I'm skeptical of its accuracy. It feels hallucinated, or like it was only based off of a few replies.

I can vouch for its accuracy.

* * *

> [@NSExceptional](#):
>
> The summary also doesn't address why this pitch hasn't moved forward, which is my real question.

It has moved forward in that the alternative design has been pitched:

> [@\[Pitch\] Last expression as return value](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958):
>
> Happy New Year, Swift Evolution! This is a new pitch for a rule that would allow multi-statement if and switch expressions, as well as introduce do expressions. It does this via a new rule that the last expression in a function or branch is the return value, without a keyword (either return for functions/closures, or the previously pitched then for if or switch). It follows from feedback from the [original pitch](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443) for a then keyword. The PR can be found [here](https://github.com/swiftlang/swift-evolution/pull/2639). Please direct typos and other minor f…

---

<div class="post-metadata">

**Author:** ![NSExceptional](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nsexceptional/32/6813_2.png) [@NSExceptional](https://forums.swift.org/u/NSExceptional)\
**Post date:** [April 28, 2025, 10:50pm UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/488 "2025-04-28T22:50:39Z")

</div>

> [@xwu](#):
>
> I can vouch for its accuracy.

🙂‍↕️  
🙏

> [@xwu](#):
>
> It has moved forward in that the alternative design has been pitched

Ah, thank you! Much appreciated ❣

---

<div class="post-metadata">

**Author:** ![JanWillemBrands](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/janwillembrands/32/14554_2.png) [@JanWillemBrands](https://forums.swift.org/u/JanWillemBrands)\
**Post date:** [May 2, 2025, 8:01am UTC](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/489 "2025-05-02T08:01:29Z")

</div>

Thanks for this prompt. Grok did very well. Should be 'just a click away'...

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