Proposal to explore Statements as Expressions
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.vscode
docs
src
.gitattributes
.gitignore
.yo-rc.json
LICENSE
README.md
gulpfile.js
package-lock.json
package.json

README.md

Proposal to Explore ECMAScript Statements as Expressions

As a follow up to the throw expressions proposal, this strawman seeks to explore the feasability of defining expression-forms for other statements. To frame this discussion and establish an overall scope for this proposal, at this time we are only considering expression forms for a limited set of statements. As such, all declarations that do not already have an expression form are out of scope for the purposes of this proposal, though they may be explored in a later proposal.

Status

Stage: 0
Champion: Ron Buckton (@rbuckton)

For more information see the TC39 proposal process.

Authors

  • Ron Buckton (@rbuckton)

Simple Statements

Simple statements are those ECMAScript statements that do not directly contain other statements (throw, return, break, continue, debugger). The completion semantics for these statements are fairly trivial to convert to expression semantics. As a result, these simple statements are the primary focus of this proposal.

throw operator

throw statements already have a proposal for an expression form, allowing the following motivating use cases:

  • Parameter initializers
    function save(filename = throw new TypeError("Argument required")) {
    }
  • Arrow function bodies
    lint(ast, { 
      with: () => throw new Error("avoid using 'with' statements.")
    });
  • Conditional expressions
    function getEncoder(encoding) {
      const encoder = encoding === "utf8" ? new UTF8Encoder() 
                    : encoding === "utf16le" ? new UTF16Encoder(false) 
                    : encoding === "utf16be" ? new UTF16Encoder(true) 
                    : throw new Error("Unsupported encoding");
    }
  • Logical operations
    class Product {
      get id() { return this._id; }
      set id(value) { this._id = value || throw new Error("Invalid value"); }
    }

Grammar

UnaryExpression[Yield, Await]:
  `throw` UnaryExpression[?Yield, ?Await]

return operator

Allowing a return in an expression position would allow for the early exit from a function while in the midst of any expression:

function checkOpts(opts) {
    let x = opts && opts.x || return false;
    /* ...do something with x... */
}

Grammar

UnaryExpression[Yield, Await]:
  `return` UnaryExpression[?Yield, ?Await]

break operator

Allowing a break in an expression position would allow for an immediate jump from within the current expression to the end of the target LabelledStatement, SwitchStatement, or IterationStatement:

switch (cond) {
  case 0:
    const y = x.y || break;
    // ...do something with `y`...
    break;
}

Grammar

UnaryExpression[Yield, Await]:
  BreakExpression

BreakExpression:
  `break`
  `break` [no |LineTerminator| here] LabelIdentifier

continue operator

Allowing a continue in an expression position would allow for an immediate jump to the next iteration within an IterationStatement:

for (const x of array) {
    const y = x.y || continue; // continue if `x.y` is not present.
}

Grammar

UnaryExpression[Yield, Await]:
  ContinueExpression

ContinueExpression:
  `continue`
  `continue` [no |LineTerminator| here] LabelIdentifier

debugger expression

Allowing a debugger operation in an expression position would allow for an immediate break into an attached debugger at the current expression:

const y = x.y || debugger; // break into debugger if `x.y` is not present.

Grammar

UnaryExpression[Yield, Await]:
  `debugger`

ExpressionStatement restrictions

To avoid grammatical ambiguity, the above expressions are restricted from ExpressionStatement:

ExpressionStatement[Yield, Await]:
  [lookahead ∉ {`{`, `function`, `async` [no |LineTerminator| here] `function`, `class`, `let [`, 
    `throw`, `return`, `break`, `continue`, `throw`, `debugger` }] Expression[+In, ?Yield, ?Await]

Compound Statements

Compound statements are those statements that directly contain one or more other statement. This category includes all control-flow statements (if, switch, try), iteration statements (do, while, for), with statements, and blocks.

This category is not yet fully explored and requires further investigation to determine what syntax and semantics would best fit each of these kinds of statements if they were to have an expression form.

This is an area that closely overlaps with the do expression proposal. A more thorough discussion regarding what constitutes a usable completion value from these statements would best be handled as part of that proposal.

Declaration Statements

Declaration statements are currently out-of-scope for this proposal, though they may be explored in a later proposal.