# 'Yielding' Conditional and Loop Expressions

**URL:** <https://es.discourse.group/t/yielding-conditional-and-loop-expressions/1837>\
**Category:** 💡 Ideas\
**Created:** [October 27, 2023, 4:40pm UTC](https://es.discourse.group/t/yielding-conditional-and-loop-expressions/1837 "2023-10-27T16:40:59Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![iansan5653](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iansan5653/32/1895_2.png) [@iansan5653](https://es.discourse.group/u/iansan5653)\
**Post date:** [October 27, 2023, 4:40pm UTC](https://es.discourse.group/t/yielding-conditional-and-loop-expressions/1837/1 "2023-10-27T16:40:59Z")

</div>

It's common to declare a variable only to immediately mutate it in a following statement:

```js
let result = 3
if (conditionA) result = 1
else if (conditionB) result = 2

```

This is typical JavaScript because support for conditional expressions that yield values is limited. But it's not ideal -- now we have a mutable `result` variable despite never planning to mutate it again. An existing alternative is the ternary expression, but it quickly becomes hard to read when nested:

```js
const result = conditionA ? 1 : (conditionB ? 2 : 3)

```

What if we could combine these two concepts, yielding a value directly from our `if` statement? This would combine the best of both options, maximizing readability while avoiding mutation:

```js
const result =
  if (conditionA) yield 2 
  else if (conditionB) yield 3
  else yield 1

```

There's no reason we have to stop at conditionals. What if we could yield `Array`s directly from loops? We could effectively combine the concepts of mapping, filtering, and flattening without having to [introduce an entirely new syntax for comprehensions](https://es.discourse.group/t/list-comprehension/112):

```js
const newUsers = for (const user of users) if (user.age > 5) yield user.id

```

Flattening comes naturally:

```js
const newUsers =
  for (const users of userGroups)
    for (const user of user)
      if (user.age > 5) yield user.id

```

The concept also works asynchronously:

```js
const resultsPromise =
  for await (const response of requests)
    if (response.success) yield response.data

```

Because they are just expressions they can be used anywhere an expression can be used, like as the return value for arrow functions:

```js
const allJson = async requests => 
  for await (const response of requests)
    if (response.ok) yield await response.json()

```

Or in JSX expressions:

```jsx
const ListOfItems = ({items}) =>
  <ul>
    { for (const item of items) if (item.isVisible) yield <li>{ item.name }</li> }
  </ul>

```

## Specification

The 'rules' around this concept would be fairly straightforward:

1. A conditional or loop statement is 'yielding' when used where an expression is expected.
2. A yielding conditional resolves to the yielded value and stops executing when a `yield` statement is encountered. If none are encountered, the expression resolves to `undefined`. A `yield*` statement is a `SyntaxError` in a yielding conditional.
3. A synchronous yielding loop always resolves to an `Array`. When a `yield` statement is encountered, the yielded value is appended to the array and execution continues. A `yield*` statement individually yields each value in the yielded iterable. If no `yield` statements are encountered, the expression resolves to `[]`.
4. An asynchronous yielding loop (`for await...of`) always resolves to `Promise<Array>`. Otherwise it acts similarly to a synchronous yielding loop.

## Alternative syntax

An alternative version of this proposal would be to introduce a new syntax for these expressions rather than inferring whether or not a statement is a yielding expression based on context. We could introduce new keywords inspired by generator function syntax, like `for*`:

```js
const newUsers = for* (const user of users) if (user.age > 5) yield user.id

```

This has the advantage of being more explicit and better distinguishing between expressions & statements, but the concept is trickier when thinking about conditionals. You could have `if*`, but then what about `else`? Maybe the whole expression is a generator conditional if it starts with `if*`:

```js
const result =
  if* (conditionA) yield 2 
  else if (conditionB) yield 3
  else yield 1

```

I don't particularly love this since it's easy to lose track in the later branches.

## Prior art

This proposal is heavily inspired by Scala. In Scala (and in Ruby), all statements are value-yielding expressions. `if` statements always yield the result of the last executed expression. This concept is nice but doesn't fit very well with JavaScript design patterns which tend to be more explicit (ie, these languages also both support implicit returns). So I think `yield` works better for JavaScript as the more explicit option.

Yielding loops are very similar to Scala's '[for comprehensions](https://docs.scala-lang.org/tour/for-comprehensions.html)', and result in a very similar syntax.

---

<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:** [October 27, 2023, 5:07pm UTC](https://es.discourse.group/t/yielding-conditional-and-loop-expressions/1837/2 "2023-10-27T17:07:26Z")

</div>

[https://github.com/tc39/proposal-do-expressions](https://github.com/tc39/proposal-do-expressions)

---

<div class="post-metadata">

**Author:** ![iansan5653](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/iansan5653/32/1895_2.png) [@iansan5653](https://es.discourse.group/u/iansan5653)\
**Post date:** [October 27, 2023, 5:27pm UTC](https://es.discourse.group/t/yielding-conditional-and-loop-expressions/1837/3 "2023-10-27T17:27:14Z")

</div>

Thanks, I hadn't seen that. `do` solves the conditional case decently. I had actually considered another alternative, 'yielding blocks':

```js
let x = *{
  let tmp = f();
  yield tmp * tmp + 1
};

```

Which is pretty much the same thing but a little more explicit about the result value.

`do` doesn't solve the looping case at all though. Maybe it makes sense to have both `do` _and_ "generator loops" with the `for*` syntax.

---

<div class="post-metadata">

**Author:** ![tabatkins](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/tabatkins/32/170_2.png) [@tabatkins](https://es.discourse.group/u/tabatkins)\
**Post date:** [October 27, 2023, 5:53pm UTC](https://es.discourse.group/t/yielding-conditional-and-loop-expressions/1837/4 "2023-10-27T17:53:16Z")

</div>

And while do-exprs will solve the problem in general, pattern-matching [https://github.com/tc39/proposal-pattern-matching](https://github.com/tc39/proposal-pattern-matching) is probably closer to ratification and solves this problem as well:

```js
const result = match(null) {
  if(conditionA): 1;
  if(conditionB): 2;
  default: 3;
}

```

(Or if the condition is actually based on some value, you can pass it to the match() instead of `null` and actually use a pattern on it.)
