Another statement expression-related proposal
# Bob Myers (9 years ago)
Just a random thought, but would {= =} work for expression blocks?
// 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)
I don't know about the loop idea. It seems like it's trying to do too much.
Just a random thought, but would `{= =}` work for expression blocks?
```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)
```
I don't know about the loop idea. It seems like it's trying to do too much.
On Wed, Nov 2, 2016 at 10:13 AM, Isiah Meadows <isiahmeadows at gmail.com>
wrote:
> 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.)
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20161102/f9343433/attachment.html># Isiah Meadows (9 years ago)
Inline.
On Wed, Nov 2, 2016, 01:01 Bob Myers <rtm at gol.com> wrote:
Just a random thought, but would
{= =}work for expression blocks?// 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)
In theory, yes, but it doesn't exactly look very pretty (too many equals signs and curly braces).
I don't know about the loop idea. It seems like it's trying to do too much.
The loop thing is a bit extra, complicating things some, but I feel it's
merited by the ability to avoid a lot of the confusion when using loops
with the current do expression proposal (loops currently act like
reduce, which is incredibly unintuitive).
Inline.
On Wed, Nov 2, 2016, 01:01 Bob Myers <rtm at gol.com> wrote:
> Just a random thought, but would `{= =}` work for expression blocks?
>
> ```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)
> ```
>
In theory, yes, but it doesn't exactly look very pretty (too many equals
signs and curly braces).
>
> I don't know about the loop idea. It seems like it's trying to do too much.
>
The loop thing is a bit extra, complicating things some, but I feel it's
merited by the ability to avoid a lot of the confusion when using loops
with the current `do` expression proposal (loops currently act like
`reduce`, which is incredibly unintuitive).
> On Wed, Nov 2, 2016 at 10:13 AM, Isiah Meadows <isiahmeadows at gmail.com>
> wrote:
>
> 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.)
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20161102/e58e8f6a/attachment.html>
Yes, there's been a bit of talk about modifying the
doproposal in various ways (like allowing more than a block, using=to differentiate, etc.), but each of these have potential ambiguities (like conflicting withdo-whileor 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 througheval) 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:// 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:: VariableStatementThese are early errors, and they are all redundant, anyways, since you already have a value you could use.
:: ContinueStatement:: BreakStatement:: ReturnStatement:: DebuggerStatement:: ThrowStatement:: EmptyStatementThese are also early errors, since there's no real value you can associate with them.
:: BlockStatement:: IfStatement:: SwitchStatement:: WithStatement:: TryStatementcompletionbe ? ExecuteStatement(statement), wherestatementis one of the statements above.completionis none, returnundefinedcompletion.:: IterationStatementstatement), wherestatementis one of the statements above.:: LabelledStatementThis works similarly, but has an outer label instead. The same above statements are also similarly banned as
LabelledItementries. 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)statementis an IterationStatement:listbe a new empty list.statement, but for eachchildstatement:resultbe ? ExecuteStatement(child).resultis not empty, then appendresulttolist.list.statementis an ExpressionStatement:statementis aBreakStatementorContinueStatement:statementstatementis aDeclaration:statement.lastbe emptystatement. For eachchildstatement:lastbe ? ExecuteStatement(child).lastis the final expression's value, oremptyif there was no final expression.last.(I probably missed several edge cases, but this is just a mailing list strawman.)
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.)