Table of Contents

Proposal

This proposal has progressed to the Draft ECMAScript 6 Specification, which is available for review here: specification_drafts. Any new issues relating to them should be filed as bugs at http://bugs.ecmascript.org. The content on this page is for historic record only and may no longer reflect the current state of the feature described within.

// Empty arrow function returns undefined
let empty = () => {};
 
// Single parameter case needs no parentheses around parameter list
let identity = x => x;
 
// No need for parentheses even for lower-precedence expression body
let square = x => x * x;
 
// Parenthesize the body to return an object literal expression
let key_maker = val => ({key: val});
 
// Statement body needs braces, must use 'return' explicitly if not void
let odds = evens.map(v => v + 1);
 
let fives = [];
nats.forEach(v => { if (v % 5 === 0) fives.push(v); });
 
// ''=>'' has only lexical ''this'', no dynamic ''this''
const obj = {
  method: function () {
    return () => this;
  }
};
assert(obj.method()() === obj);
 
let fake = {steal: obj.method()};
assert(fake.steal() === obj);
 
// But ''function'' still has dynamic ''this'' of course
let real = {borrow: obj.method};
assert(real.borrow()() === real);

Grammar Changes

Extend AssignmentExpression and define ArrowFunctionExpression:

AssignmentExpression :
    ArrowFunctionExpression
    ...

ArrowFunctionExpression :
    ArrowFormalParameters => [lookahead ∉ { "{" }] AssignmentExpression
    ArrowFormalParameters => Block

ArrowFormalParameters :
    ( FormalParameterList_opt )
    Identifier

Arrow functions bind this lexically, bind return in the Block body case so it returns from the immediately enclosing arrow function, and preclude break and continue from referencing statements outside the immediately enclosing arrow function.

The Identifier primary expression arguments may not be used in an arrow function’s body (whether expression or block form).

Likewise, yield may not be used in an arrow function’s body. Arrows cannot be generators and we do not want deep continuations.

Arrow functions are like built-in functions in that both lack .prototype and any [[Construct]] internal method. So new (() => {}) throws a TypeError but otherwise arrows are like functions:

assert(typeof () => {} === "function");
assert(Object.getPrototypeOf(() => {}) === Function.prototype);

Because this is lexically bound, arrow.call and arrow.apply cannot bind a different this parameter value, but they can pass arbitrary arguments, of course.

The ArrowFormalParameters production requires GLR parsing or equivalent to disambiguate against the other right-hand sides of AssignmentExpression. For an LR(1) grammar, we can use:

ArrowFormalParameters :
    ( Expression_opt )

and write Supplemental Syntax to require that Expression reductions match FormalParameterList.

This works because Expression is a cover grammar for FormalParameterList, with Identifier primary expressions covering formal parameter names, array and object literals for destructuring, assignment for parameter default values, and spread for rest parameters.

This cover grammar approach may require, e.g., extending guards to be legal in expressions if we add guards. We can cross that bridge later if necessary.

These changes are intended to be backward-compatible: existing JS parses as before, with the same semantics. New opt-in Harmony JS may use arrow functions where allowed.

Rationale

  • Hard to beat C# and CoffeeScript here (we want the unparenthesized single-parameter form as in C#).
    • TC39 should embrace, clean-up, and extend rather than re-invent or compete with de-facto and nearby de-jure standards.
    • However, we don’t want CoffeeScript’s ->, it’s confusing to have two arrows and dynamic this binding is an oft-fired footgun.
    • Currently we don’t make () or {} in () => {} optional, but we could at risk of losing consensus (but no grammatical bugs bite optionality here).
  • Lexical this matches the dominant cohort measured by Kevin Smith (see the BTF Measurements thread) that either does not use this or wants it lexically bound.
    • Best results after modifying subject code to use method definition shorthand and only then scanning for function.
    • From lexical this it follows that arrow functions are not constructors (no .prototype or [[Construct]]).
  • => parses as if it were a low-precedence (assignment) operator joining a restricted comma expression (implicitly quoted) to a body.
    • This avoids precedence inversion when the body is an AssignmentExpression.
    • No preference for object literal over block (see block vs object literal to keep grammar simple and match expression-statement.
  • TCP compliance “above” expressions – for statements and programs – looks like a mismatch with JS functions, old-style or arrow.
    • return is problematic because users want to write early returns in async callbacks, which under TCP would throw.
    • break and continue in sync callbacks could work well but would reify as exceptions and be observable via finally even if uncatchable.
    • In general the statements vs. expressions rift in JS, inherited via Java from C, means full TCP creates inevitable confusion and bugs.

Brendan Eich 2012/03/29 19:36

Deferred

  • Optional leading (this Initialiseropt) parameter:
    • for explicit dynamic-this with soft_bind opt-in;
    • in lieu of -> shorthand, to avoid having two arrows.
  • Optional leading Identifier for named arrow function expressions.
  • Optional leading # for deep freezing.

See Also

block lambda revival, an alternative adding new semantics, not only new syntax

 
harmony/arrow_function_syntax.txt · Last modified: 2013/07/11 23:59 by rwaldron
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki