# Return-as-expression strawman

**URL:** <https://es.discourse.group/t/return-as-expression-strawman/1884>\
**Category:** 💡 Ideas\
**Created:** [December 26, 2023, 9:14pm UTC](https://es.discourse.group/t/return-as-expression-strawman/1884 "2023-12-26T21:14:13Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [December 26, 2023, 9:14pm UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/1 "2023-12-26T21:14:13Z")

</div>

I don't expect this probably has any place in ECMAScript (sadly) because I can't figure out a way to syntax it without it being confusing, but I wanted to get some input on a nice-to-have that I've wanted for some time now. The idea is to change the semantics of `return` to be a unary prefix operator of lowest precedence that:

- evaluates to its argument
- stores its argument in the current function's "return slot"
- triggers end-of-function at the end of the current statement

Which is a strict superset of the current return semantics. The times when I wish for it hardest are when I'm debugging and I want to trace function entry/exit:

```js
// original function:
function sumOrNone(x, y) {
    if (x === null) return y;
    if (y === null) return x;
    return x + y;
}

//add debug tracing, return-as-expression syntax:
function sumOrNone(x, y) {
    console.log("Called sumOrNone with arguments:", {x, y});
    if (x === null) console.log("x is null, returning y:", return y);
    if (y === null) console.log("y is null, returning x:", return x);
    console.log("neither is null, adding", return x + y);
}

//add debug tracing, current syntax:
function sumOrNone(x, y) {
    console.log("Called sumOrNone with arguments:", {x, y});
    if (x === null) {
        console.log("x is null, returning y:", y);
        return y;
    }
    if (y === null) {
        console.log("y is null, returning x:", x);
        return x;
    }
    const rv = x + y;
    console.log("neither is null, adding", rv);
    return rv;
}

```

With return-as-expression, if I have a function with a ton of return statements, I can instantly add logging to every one of them with a single find/replace. As it is now, I have to add braces, store return values, etc.

The downside is that it becomes harder to find which statements are possible function returns, as "return" can become buried and/or fall off the right edge of the screen, but that comes down to code style - it's already possible to put return in weird places, especially if you combine multiple statements per line. Not to mention, of course, that the semantics would need some serious hammering-on, like, what happens when a return expression is part of an await? What happens when you have multiple return expressions in a single statement?

I can't think of any direct piece of prior art, but C#'s `throw` expressions are similar (usually as a way to "abort with exception if this parameter is invalid", and `return` expressions could be used in a similar way for a more graceful function abort. The difference between the two is pretty huge, though: `throw` always aborts instantly, whereas `return`-as-expression has to defer the return until after the current statement is complete.

Thoughts?

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [December 27, 2023, 10:31am UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/2 "2023-12-27T10:31:56Z")

</div>

A solution using existing techniques:

```javascript
function trace(log, v) {
  console.log(log, v);
  return v;
}

function sumOrNone(x, y) {
    console.log("Called sumOrNone with arguments:", {x, y});
    if (x === null) return trace("x is null, returning y:", y);
    if (y === null) return trace("y is null, returning x:", x);
    return trace("neither is null, adding", x + y);
}

```

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [December 27, 2023, 10:40am UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/3 "2023-12-27T10:40:31Z")

</div>

If there was [GitHub - tc39/proposal-pipeline-operator: A proposal for adding a useful pipe operator to JavaScript.](https://github.com/tc39/proposal-pipeline-operator)

```javascript
function sumOrNone(x, y) {
    console.log("Called sumOrNone with arguments:", {x, y});
    if (x === null) return y |> (console.log("x is null, returning y:", ^^), ^^);
    if (y === null) return x |> (console.log("y is null, returning x:", ^^), ^^);
    return x + y |> (console.log("neither is null, adding", ^^) ^^);
}

```

Or combined with the trace function declaration before above:

```javascript
function sumOrNone(x, y) {
    console.log("Called sumOrNone with arguments:", {x, y});
    if (x === null) return y |> trace("x is null, returning y:", ^^);
    if (y === null) return x |> trace("y is null, returning x:", ^^);
    return x + y |> trace("neither is null, adding", ^^);
}

```

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [December 27, 2023, 3:44pm UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/4 "2023-12-27T15:44:53Z")

</div>

If the following proposal were to go through in it's current form, it would add a trace function like that, but it would be called "debugger.log()" instead: [GitHub - tc39/proposal-standardized-debug: Standardized debug](https://github.com/tc39/proposal-standardized-debug)

---

<div class="post-metadata">

**Author:** ![jithujoshyjy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jithujoshyjy/32/399_2.png) [@jithujoshyjy](https://es.discourse.group/u/jithujoshyjy)\
**Post date:** [December 29, 2023, 11:11am UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/5 "2023-12-29T11:11:02Z")

</div>

I think this is a good place to clear a doubt of mine; Was there ever a thought/consideration to use return as an expression similar to [proposal-throw-expressions](https://github.com/tc39/proposal-throw-expressions)?

It could be beneficial in all the places that proposal is looking at. And what about other jump statements like `break` and `continue`?

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [December 29, 2023, 4:27pm UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/6 "2023-12-29T16:27:12Z")

</div>

If a return-as-expression (or continue/break) were to behave in a way that was similar to throw-as-expression, then it would behave differently from what the O.P. described. More precisely,

```javascript
function doit() {
  console.log('value is', return 2);
}

```

would return the number 2, but wouldn't log anything (when the return gets hit, the function immedietally returns). This is the same with how

```javascript
function doit() {
  console.log('value is', throw new Error('!!!'));
}

```

will throw an error, and nothing will get logged. (when the throw gets hit, the error gets immediately thrown - no further things are evaluated in the function).

For the O.P.'s specific use case, I think some sort of trace()/debugger.log() method would work equally well, and is simpler to understand (doesn't require new syntax).

---

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [December 29, 2023, 7:12pm UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/7 "2023-12-29T19:12:46Z")

</div>

> [@theScottyJam](#):
>
> For the O.P.'s specific use case, I think some sort of trace()/debugger.log() method would work equally well, and is simpler to understand (doesn't require new syntax).

You're not wrong, and I swear I've had other use cases in mind (this isn't an infrequent wish of mine) but I haven't been able to remember what they are with my memory as fallible as it is 😅

---

<div class="post-metadata">

**Author:** ![jithujoshyjy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jithujoshyjy/32/399_2.png) [@jithujoshyjy](https://es.discourse.group/u/jithujoshyjy)\
**Post date:** [December 31, 2023, 4:14am UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/8 "2023-12-31T04:14:55Z")

</div>

I think if return as expression works similar to throw expression proposal then I think it will have even greater implications and use cases than what OP mentioned.  
For example:

```javascript
function greetIfOdd(number) {
    console.log(number % 2 != 0
        ? "Hello Odd one👋"
        : return
    )
}

```

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [December 31, 2023, 4:34am UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/9 "2023-12-31T04:34:17Z")

</div>

To me this is a very strong argument against ever allowing it, since I find that incredibly hard to read and understand. Having exceptions be throwable at any point is hard enough; having to track normal completions coming from anywhere would be much harder.

---

<div class="post-metadata">

**Author:** ![jithujoshyjy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jithujoshyjy/32/399_2.png) [@jithujoshyjy](https://es.discourse.group/u/jithujoshyjy)\
**Post date:** [December 31, 2023, 6:24am UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/10 "2023-12-31T06:24:07Z")

</div>

Can you give an instance where it might mislead/confuse people?

My take is that continuation calls should be malleable like any other language feature even if that means "With great power comes greater complexity"

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [December 31, 2023, 6:38am UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/11 "2023-12-31T06:38:42Z")

</div>

Greater complexity should be avoided, imo.

---

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [December 31, 2023, 9:04pm UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/12 "2023-12-31T21:04:07Z")

</div>

I think this is a good enough reason to abandon this idea, because this doesn't do what you think it does given my original proposed semantics - it'll instead print out undefined, not abort the statement 😅

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [January 2, 2024, 9:19pm UTC](https://es.discourse.group/t/return-as-expression-strawman/1884/13 "2024-01-02T21:19:09Z")

</div>

Having done a lot of Rust lately, I'll agree to disagree here. In Rust, it's very common, with macros and even dedicated syntax (`expr?`, used to be a macro) to conditionally return. And I have no problem tracing the control flow.

If control flow tracking is truly a concern, I'll just use if/else and explicit assignment. If it hinders readability, I can just not use it. It's pretty easy to avoid when it's most problematic in my experience.
