# \[Pitch\] Last expression as return value

**URL:** <https://forums.swift.org/t/pitch-last-expression-as-return-value/76958>\
**Category:** Pitches\
**Created:** [January 2, 2025, 12:37am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958 "2025-01-02T00:37:10Z")\
**Posts on this page:** 20\
**Page:** 7

<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 4, 2025, 3:40pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/126 "2025-01-04T15:40:25Z")

</div>

It is fine to have something un-named, if it is fundamental, it has been conceived early in the language design, and it fits to the overall language style. E.g., some strange language may decide to write `x value` where most others write `x = value` or `x := value`.

The problems start when they want to have a second un-named thing (mostly because they cannot think of a good name or symbol).

In the previous Pitch there was the argument "no other keywords, we have enough". I invert this argument: assume that you remove 10 of the many keywords and make their features un-named. How would the language look like then?

---

<div class="post-metadata">

**Author:** ![carlynorama](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/carlynorama/32/27072_2.png) [@carlynorama](https://forums.swift.org/u/carlynorama)\
**Post date:** [January 4, 2025, 4:04pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/127 "2025-01-04T16:04:08Z")

</div>

I think this is where our arguments with the current pitch differ, I think. I wouldn't have wanted this unnamed even from the beginning, I don't think.

Yes absolutely, readability bad, compiler nightmare, confusing change... honestly don't disagree with any of that... but already that's been called "fear mongering", so, you know, doesn't sound like those arguments are quite sinking in.

For me the big issue is that Swift makes me jump through a lot of hoops I don't have to in other languages which are annoying in the moment but make my code so much more stable and harder to hack.

Along those lines I want to be asked for affirmative positive consent before information changes scope.

Other things can be unnamed, sure. But this can't if safety matters. Can it? Where is developer consent that this information is safe to move?

It turns Swift greedy and permissive in a way that feels out of character. Rust does not do this. It has the missing Semi-colon. It disrupts my sense of Swift as a safety focused language.

Something trivial, like a colon before, idk. Something. Give us something.

I've given up on arguing against multiline because that's clearly a loosing battle, but I'd prefer it if Swift didn't take away requiring consent to move the information to a new scope. I hate that ResultBuilders allow this, but sailed ships and all.

---

<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 4, 2025, 6:41pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/129 "2025-01-04T18:41:46Z")

</div>

I imagine that after a while, using the last expression as the result might feel very natural and not just like a "hack" for lazy people like me... Along with a certain syntax and its semantics, a certain "feeling" or mental model emerges. A stack of statements or expressions might yield a stack of values, most of which might be nothing or a discardable result, and the function "naturally" returns the top value of that stack (unlike result builders that take all values)... Such additions change your perception of the programming language, and with this different perception, some of the arguments presented ~~will no longer be the same~~ [**updated formulation:**] might appear in a different light to some people. Just a thought.

---

<div class="post-metadata">

**Author:** ![carlynorama](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/carlynorama/32/27072_2.png) [@carlynorama](https://forums.swift.org/u/carlynorama)\
**Post date:** [January 4, 2025, 7:59pm UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/130 "2025-01-04T19:59:44Z")

</div>

I'm not going to pick up my toys and run away if this proposal passes. I'll be happy for the people for whom it has made happy. I will think a mistake has been made, but I am, in fact, an adaptable sort.

I think the mental model you're suggesting will come to "feel" "natural" is a bad one for a language that values safety.

We can disagree on that. 100%. And the community will decide what it wants.

That said, I don't find "Relax. Your objections are just fluffy things that will go away. You'll get used to it " particularly rhetorically compelling, respectful or perhaps as reassuring as maybe genuinely intended.

Just a thought. Have a great weekend.

---

<div class="post-metadata">

**Author:** ![austintatious](https://avatars.discourse-cdn.com/v4/letter/a/ce73a5/32.png) [@austintatious](https://forums.swift.org/u/austintatious)\
**Post date:** [January 5, 2025, 12:29am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/131 "2025-01-05T00:29:21Z")

</div>

-1 I can’t imagine writing large functions that process a lot of data (let’s say a string) but also uses functions with @discardableResult that happen to return the same value as the function it is called in, comment out some code out (because that happens when developing) only to find out it is returning some data that some unexpected function returned midway through the function before all the comments not realizing you commented out the return value.

This last expression as return value is also going to have semantic exceptions like the body of a result builder which could be confusing.

It is so simple to understand why this would be allowed in single line closures/function bodies that are easy to debug and keep track of.

---

<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:** [January 5, 2025, 12:32am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/132 "2025-01-05T00:32:47Z")

</div>

> [@beccadax](#):
>
> What’s the plan for loops? What about if a control flow statement, like `break` or `continue`, is the last thing to be executed? If loops or control flow statements are banned, that means this proposal doesn’t get us to “literally everything is an expression”, so are we actually going to be happier with this resting place?

That's an interesting angle. Potential line of reasoning here could be:

> [@](#):
>
> 1. we also need this feature for for loops
> 2. we'd like to make it better than what we could currently do with `map` or `compactMap` (e.g. to have an ability to make an array of optional values).
> 3. but how?
> 
> - possible option 3a:
> 
> ```swift
> let items: [T?] = for i in 0 ..< 1000 { i in
> if condition(i) {
> foo(i) // foo returns `T?`
> } // no "else" here to mean "no value is produced"
> }
> 
> ```
> 
> - possible option 3b:
> 
> ```swift
> let items: [T?] = for i in 0 ..< 1000 { i in
> if condition(i) {
> some_keyword_here foo(i) // foo returns `T?`
> }
> }
> 
> ```
> 
> whereas "some\_keyword\_here" is what we discussed in the previous pitch (like yield, produce, etc).
> 
> If option 3a is taken we'd need to make some revisions to what "if expression" means (as written it's not). And if option 3b is taken then the logical outcome is to use that same "some\_keyword\_here" for if/switch/do" expressions. And if we proceed with the current pitch we might be closing the door for option 3b (that we would be able to take otherwise).

That's of course if we'll need this feature for loops..

---

<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 5, 2025, 2:12am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/133 "2025-01-05T02:12:53Z")

</div>

Another option is to collect only the first value in a loop, which makes more sense if "some\_keyword\_here" means "return". This option would need an extension of the syntax with an else part (see [comment in previous pitch](https://forums.swift.org/t/pitch-multi-statement-if-switch-do-expressions/68443/478)).  
It would also allow while loops, which may not end otherwise. Or double loops, or more complex control structures.

---

<div class="post-metadata">

**Author:** ![DandyLyons](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dandylyons/32/14417_2.png) [@DandyLyons](https://forums.swift.org/u/DandyLyons)\
**Post date:** [January 5, 2025, 2:52am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/134 "2025-01-05T02:52:38Z")

</div>

Some of my concerns raised [here](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/38) and [here](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/44) have been addressed, but others remain unaddressed.

In short, my primary concern with this proposal is that it will generate many surprising side effects that will be jarring to both new and long-time users. We are implicitly returning values which changes the inferred type.

For a function this is not a big deal, since every func must explicitly declare its return type and it must agree with the implementation's return value.

For an if/switch/do expression, this is a bigger deal since the returned type need not be declared explicitly. So a small change can seem odd.

```swift
var childSafety = true

let carBackDoorLabel = switch carBackDoorButtonAction {
  case .lock: 
    "Locked"
  case .unlock: 
    if childSafety { // this is an if expression inside a switch expression. 
      "Locked"
    } else {
      "Unlocked"
    }
}
print("The lock on the back door of the car is now \(carBackDoorLabel)")

```

This example is obviously contrived but it shows how easy it is to abuse this. If I wrote the above, and then later needed to add print statements to debug, then I'd get different results based simply on line order.

```swift
var childSafety = true

let carBackDoorLabel = switch carBackDoorButtonAction {
  case .lock: 
    print("childSafety: \(childSafety)")
    "Locked"
  case .unlock: 
    if childSafety { // this is an if expression inside a switch expression. 
      "Locked"
    } else {
      "Unlocked"
    }
    print("childSafety: \(childSafety)")
}
// 🔴 All branches of a switch expression must evaluate to the same type. 

```

Even worse, if I simply change the order in this way, I get no error, but the switch expression evaluates to the type `Void` which would be very surprising.

```swift
var childSafety = true

let carBackDoorLabel = switch carBackDoorButtonAction {
  case .lock: 
    "Locked"
    print("childSafety: \(childSafety)")
  case .unlock: 
    if childSafety { // this is an if expression inside a switch expression. 
      "Locked"
    } else {
      "Unlocked"
    }
    print("childSafety: \(childSafety)")
}
// 🔴 All branches of a switch expression must evaluate to the same type. 

```

In most languages, it is not expected that simply adding a print statement can completely change the type, or worse yet, make your code not even compile!

It is also not required to explicitly declare the return type for a closure, so it is not hard to imagine that this same scenario could easily lead to surprising inferred types for closures. Except with closures, the user already has to juggle the cognitive overload of everything else in the closures type definition including the input parameters, capture list, `@escaping`, and concurrency isolation.

For those who are in favor of this feature, I also see the potential benefits, but I think there are a lot of new ambiguities and rough edges that it would create that we have yet to discover, let alone mitigate. At the moment, I'm not convinced that the pros outweigh the cons. I would like to be wrong.

---

<div class="post-metadata">

**Author:** ![DandyLyons](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dandylyons/32/14417_2.png) [@DandyLyons](https://forums.swift.org/u/DandyLyons)\
**Post date:** [January 5, 2025, 2:57am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/135 "2025-01-05T02:57:54Z")

</div>

As contrived as my last example was, it's worth remembering that it is **very** common for users of Result Builder APIs like SwiftUI to generate very deeply nested code. The vast majority of it is closures with inferred types, which is precisely what this proposal makes more ambiguous.

Ambiguity is a threefold problem:

1. It makes code more difficult to understand for humans
2. It makes the code more difficult to parse for the compiler. **Remember. Swift is notorious for extremely long compile times, and obtuse type errors already.**
3. It makes it very easy for the human to accidentally write valid code that means something totally different than what they thought it meant. (Like the example above.)

---

<div class="post-metadata">

**Author:** ![DandyLyons](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dandylyons/32/14417_2.png) [@DandyLyons](https://forums.swift.org/u/DandyLyons)\
**Post date:** [January 5, 2025, 3:08am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/136 "2025-01-05T03:08:15Z")

</div>

It's difficult for me to imagine a scenario where this proposal doesn't significantly increase compile times. It looks like it exponentially increases the branches of scenarios that the compiler must check for. It effectively turns almost any `if` and `switch` control flow into an expression that must be type-checked and validated on all branches (no matter how deeply nested).

**Do we have any system in place to check the compile time of a new proposal implementation before it has been fully accepted?** If this proposal increases compile times more than 10% then that is already reason enough not to accept it IMHO.

---

<div class="post-metadata">

**Author:** ![austintatious](https://avatars.discourse-cdn.com/v4/letter/a/ce73a5/32.png) [@austintatious](https://forums.swift.org/u/austintatious)\
**Post date:** [January 5, 2025, 3:20am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/137 "2025-01-05T03:20:58Z")

</div>

I just also think it would be odd that you'd have a function with all these `return` statements in it, only for the last one to be elided -- for what purpose? So 6 letters don't have to be typed?

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [January 5, 2025, 3:27am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/138 "2025-01-05T03:27:58Z")

</div>

Note that this code generates multiple warnings:

> [@DandyLyons](#):
>
> ```swift
> var childSafety = true
> 
> // Constant 'carBackDoorLabel' inferred to have type '()', 
> // which may be unexpected
> let carBackDoorLabel = switch carBackDoorButtonAction {
> case .lock: 
> "Locked" // String literal is unused
> print("childSafety: \(childSafety)")
> case .unlock: 
> if childSafety {
> "Locked" // String literal is unused
> } else {
> "Unlocked" // String literal is unused
> }
> print("childSafety: \(childSafety)")
> }
> 
> ```

It is possible to construct an even more contrived example, involving every branch having a discardable result. But the variable of void type does not go away.

Note also that the value will be used shortly after, and it becomes still less likely that the code will compile with the mistake going unnoticed (you _might_ put the wrongly-typed value into an `Any`, propagating the error further)

> [@DandyLyons](#):
>
> In most languages, it is not expected that simply adding a print statement can completely change the type, or worse yet, make your code not even compile!

The "worse yet, make your code not even compile!" comment here is a little odd. Note that _today_ adding a print statement can do this when added to an `if` expression. This is the problem this proposal aims to resolve.

It does not seem like a reasonable argument against a language proposal to suggest that altering an code to be incorrect is bad _because the invalid code doesn't compile_.

---

<div class="post-metadata">

**Author:** ![DandyLyons](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dandylyons/32/14417_2.png) [@DandyLyons](https://forums.swift.org/u/DandyLyons)\
**Post date:** [January 5, 2025, 3:33am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/139 "2025-01-05T03:33:47Z")

</div>

It's worth repeating that Result Builders still pose a problem for this pitch, and I haven't yet seen a good answer to this concern. Today, we already have at least 3 very different paradigms that are semantically expressed with the same language-level control-flow primitives `if` and `switch`:

1. plain-old control flow
2. Result Builders. This looks like control flow, but it's definitely not.
3. `if` and `switch` expressions. This also looks like control flow, but it's not (today). But after this pitch, it will **also** be allowed to include control flow and other statements?

At the call-site these three paradigms are often indistinguishable. Here's one of the first things you learn in SwiftUI:

```swift
struct MyView: View {
  @State private var showingText = false
  var body: some View {
    if showingText {
      Text("Now you can see the text")
    } 
    Button(if showingText { "Hide" } else { "Show" }) {
      showingText.toggle()
    }
  }
}

```

Which paradigm is the `if` using now? 2? 3?

If it's an `if` expression, then that's not valid because all branches have to be the same type and `Text` and `Button` are not the same type.

If it's a Result Builder (like it is today), then all the branches do **not** have to be the same type. So it would be valid. But why **wouldn't** it be using paradigm 3? If a junior dev, or a new Swift learner mistakenly thinks that it's using paradigm 3, when it's actually using 2, then how do we explain to them how to know which paradigm applies and when?

---

<div class="post-metadata">

**Author:** ![DandyLyons](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dandylyons/32/14417_2.png) [@DandyLyons](https://forums.swift.org/u/DandyLyons)\
**Post date:** [January 5, 2025, 3:42am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/140 "2025-01-05T03:42:35Z")

</div>

> [@ben-cohen](#):
>
> The "worse yet, make your code not even compile!" comment here is a little odd. Note that _today_ adding a print statement can do this when added to an `if` expression. This is the problem this proposal aims to resolve.

Yes, the print statement here is already invalid _today_ inside an `if` expression. But the point is that this proposal makes it much much harder to tell if you are looking at an `if` statement or an `if` expression. This `print` would **not** be invalid today in an `if` expression

> [@ben-cohen](#):
>
> It does not seem like a reasonable argument against a language proposal to suggest that altering an code to be incorrect is bad _because the invalid code doesn't compile_.

This is a really good callout and I do agree with you. To clarify, I'm not trying to argue that "altering an code to be incorrect is bad _because the invalid code doesn't compile_". What I'm trying to argue is that changing a very fundamental part of the language such as basic control flow is a very big deal, and it breaks every users mental model. It's not hard to imagine more scenarios where I could write code that looks like an `if` statement and would be valid if it were a statement.

---

<div class="post-metadata">

**Author:** ![DandyLyons](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dandylyons/32/14417_2.png) [@DandyLyons](https://forums.swift.org/u/DandyLyons)\
**Post date:** [January 5, 2025, 3:46am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/141 "2025-01-05T03:46:05Z")

</div>

> [@ben-cohen](#):
>
> Note that this code generates multiple warnings:

Yes absolutely. This code generates multiple warnings. And none of them would seem to hint to the user that line order is actually the problem.

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [January 5, 2025, 3:51am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/142 "2025-01-05T03:51:48Z")

</div>

> [@DandyLyons](#):
>
> This code generates multiple warnings. And none of them would seem to hint to the user that line order is actually the problem.

I disagree – the warnings very clearly indicate the nature of the problem that has been introduced and caused them.

> [@DandyLyons](#):
>
> It's not hard to imagine more scenarios where I could write code that looks like an `if` statement and would be valid if it were a statement.

It seems fairly hard, because this thread doesn't contain one, despite multiple comments that they exist.

---

<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 5, 2025, 3:57am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/143 "2025-01-05T03:57:30Z")

</div>

The syntax of if/switch expressions is quite clear. An `=` sign followed by `if` is paradigm 3. However I agree with you, more complex code can be confusing or even ambiguous. The intention is to simplify multiple choice expressions, but the reuse of known keywords makes the implementation confusing.

---

<div class="post-metadata">

**Author:** ![DandyLyons](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dandylyons/32/14417_2.png) [@DandyLyons](https://forums.swift.org/u/DandyLyons)\
**Post date:** [January 5, 2025, 4:13am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/144 "2025-01-05T04:13:55Z")

</div>

So there's a problem with my earlier [code example](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/139):

```swift
struct MyView: View {
  @State private var showingText = false
  var body: some View {
    if showingText {
      Text("Now you can see the text")
    } 
        // 👇🏼 🔴: `if` may only be used as expression in return, throw, or as the source of an assignment
    Button(if showingText { "Hide" } else { "Show" }) {
      showingText.toggle()
    }
  }
}

```

It yields the error `'if' may only be used as expression in return, throw, or as the source of an assignment`. This error is very helpful, and I wasn't aware of (or must have forgotten) this limitation. This limitation does greatly reduce the surface area of potential ambiguities. Which is good. As @georgeef says "An = sign followed by if is paradigm 3 [an `if` expression]."

Assuming this limitation is never lifted (say if someone in the future wants to remove the inconsistency between ternaries and `if` expressions) then I'd say that addresses most of my concerns.

---

<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 5, 2025, 4:24am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/145 "2025-01-05T04:24:03Z")

</div>

With another keyword instead of `if`, your example could be valid and meaningful. With the keyword `if` and without any other syntax help, it can only be confusing.

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [January 5, 2025, 4:27am UTC](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958/146 "2025-01-05T04:27:59Z")

</div>

> [@DandyLyons](#):
>
> Assuming this limitation is never lifted (say if someone in the future wants to remove the inconsistency between ternaries and `if` expressions) then I'd say that addresses most of my concerns.

FWIW my personal preference is not to extend `if` expressions to appear in all positions. Ternaries are better for most of the desirable cases, others would be clearer broken up, and if anything, multi-statement `if` expressions make them less suitable to be used in e.g. argument position.

[Previous page](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958.md?page=6)

[Next page](https://forums.swift.org/t/pitch-last-expression-as-return-value/76958.md?page=8)
