Another statement expression-related proposal

Isiah Meadows isiahmeadows at gmail.com
Wed Nov 2 04:43:46 UTC 2016


Yes, there's been a bit of talk about modifying the `do` proposal in
various ways (like allowing more than a block, using `=` to
differentiate, etc.), but each of these have potential ambiguities
(like conflicting with `do-while` or object literal syntax,
respectively), and give unintuitive results with loops.

Here's my idea: prepend a `::` to any non-expression statement, and it
becomes an expression. The last statement's value (as if through
`eval`) is used as the return value, with exception of loops, which
return an array generated from their loop body. Here's a few examples
to clarify:

```js
// plain block
const foo = :: { let a = 1; a }
assert.equal(foo, 1)

// if-else
let cond = false
const bar = :: if (cond) "hi" else "bye"
assert.equal(bar, "bye")

// try-catch
let e = new Error()
const error = :: try { throw e } catch (e) { e }
assert.equal(error, e)

// while loop
let i = 0
const range = :: while (i < 10) i++
assert.deepEqual(range, [
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
])

// for + nested if-else
const fizzBuzz = :: for (let i = 0; i < 100; i++) {
  if (i % 15) "FizzBuzz"
  else if (i % 3) "Fizz"
  else if (i % 5) "Buzz"
  // If no expression is evaluated in the body, then no value
  // is pushed.
}
assert.deepEqual(fizzBuzz, [
  // 100 lines of the classic "Fizz", "Buzz", and "FizzBuzz"
])
```

What do you all think of this?

-----------

To clarify, here's some more detailed semantics:

### Grammar

- `:: ExpressionStatement`
- `:: Declaration`
- `:: VariableStatement`

These are early errors, and they are all redundant, anyways, since you
already have a value you could use.

- `:: ContinueStatement`
- `:: BreakStatement`
- `:: ReturnStatement`
- `:: DebuggerStatement`
- `:: ThrowStatement`
- `:: EmptyStatement`

These are also early errors, since there's no real value you can
associate with them.

- `:: BlockStatement`
- `:: IfStatement`
- `:: SwitchStatement`
- `:: WithStatement`
- `:: TryStatement`

1. Let `completion` be ? ExecuteStatement(`statement`), where
`statement` is one of the statements above.
2. If `completion` is *none*, return `undefined`
3. Otherwise, return `completion`.

- `:: IterationStatement`

1. Return ? ExecuteIterationStatement(`statement`), where `statement`
is one of the statements above.

- `:: LabelledStatement`

This works similarly, but has an outer label instead. The same above
statements are also similarly banned as `LabelledItem` entries.
Additionally, the Annex B extension for FunctionDeclaraions are also
early errors.

(Should these even be allowed? I'm open to making this an early error instead.)

### Abstract Operation ExecuteStatement(`statement`)

1. If `statement` is an IterationStatement:
  1. Let `list` be a new empty list.
  2. Evaluate `statement`, but for each `child` statement:
    1. Let `result` be ? ExecuteStatement(`child`).
    2. If `result` is not *empty*, then append `result` to `list`.
  3. Return `list`.
2. If `statement` is an ExpressionStatement:
  1. Return the result of evaluating the expression within.
3. If `statement` is a `BreakStatement` or `ContinueStatement`:
  1. Evaluate `statement`
  2. Return *empty*.
4. If `statement` is a `Declaration`:
  1. Evaluate `statement`.
  2. Return the value of the resulting declaration
5. Otherwise:
  1. Let `last` be *empty*
  2. Evaluate `statement`. For each `child` statement:
    1. Let `last` be ? ExecuteStatement(`child`).
  3. Note: `last` is the final expression's value, or `empty` if there
was no final expression.
  4. Return `last`.

(I probably missed several edge cases, but this is just a mailing list
strawman.)


More information about the es-discuss mailing list