[Arrow function syntax] Move Arrow to head to fix ArrowFormalParameters issues

Sean Eagan seaneagan1 at gmail.com
Wed Jun 1 08:30:33 PDT 2011


Why not just move Arrow to the head to avoid any current or future
ArrowFormalParameters parsing issues such as the GLR issue ?


ArrowFunctionExpression :
    Arrow ArrowFormalParametersOpt InitialValue
    Arrow ArrowFormalParametersOpt BlockOpt


This would deviate from C# and Coffeescript tradition, but I think
even for people familiar with those languages it would be an easy
transition.  They could potentially be called "arrowhead" functions as
a mnemonic device for the arrow position if necessary.

Here's what the examples from the arrow function syntax strawman [1]
would then look like:

// Empty arrow function is minimal-length
let empty = ->;

// Expression bodies needs no parentheses or braces
let identity = -> (x) x;

// Fix: object initialiser need not be parenthesized, see Grammar Changes
let key_maker = -> (val) {key: val};

// Nullary arrow function starts with arrow (cannot begin statement)
let nullary = -> preamble + ': ' + body;

// No need for parens even for lower-precedence expression body
let square = -> (x) x * x;

// Statement body needs braces, must use 'return' explicitly if not void
let oddArray = [];
array.forEach( -> (v, i) { if (i & 1) oddArray[i >>> 1] = v; });

// Use # to freeze and join to nearest relevant closure
function return_pure() {
  return # -> (a) a * a;
}

let p = return_pure(),
    q = return_pure();
assert(p === q);

function check_frozen(o) {
  try {
    o.x = "expando";
    assert(! "reached");
  } catch (e) {
    // e is something like "TypeError: o is not extensible"
    assert(e.name == "TypeError");
  }
}

check_frozen(p);

function partial_mul(a) {
  return # -> (b) a * b;
}

let x = partial_mul(3),
    y = partial_mul(4),
    z = partial_mul(3);

assert(x !== y);
assert(x !== z);
assert(y !== z);

check_frozen(x);
check_frozen(y);
check_frozen(z);

// Use ''=>'' (fat arrow) for lexical ''this'', as in CoffeeScript
// ("fat" is apt because this form costs more than ''->'')
const obj = {
  method: function () {
    return => this;
  }
};
assert(obj.method()() === obj);

// And *only* lexical ''this'' for => functions
let fake = {steal: obj.method()};
assert(fake.steal() === obj);

// But ''function'' still has dynamic ''this''
let real = {borrow: obj.method};
assert(real.borrow()() === real);

// Recap:
//  use ''->'' instead of ''function'' for lighter syntax
//  use ''=>'' instead of calling bind or writing a closure
const obj2 = {
  method: -> () (=> this)
};
assert(obj2.method()() === obj2);

let fake2 = {steal: obj2.method()};
assert(fake2.steal() === obj2);

let real2 = {borrow: obj2.method};
assert(real2.method()() === real2);

// An explicit ''this'' parameter can have an initializer
// Semantics are as in the "parameter default values" Harmony proposal
const self = {c: 0};
const self_bound = -> (this = self, a, b) {
  this.c = a * b;
};
self_bound(2, 3);
assert(self.c === 6);

const other = {c: "not set"};
self_bound.call(other, 4, 5);
assert(other.c === "not set");
assert(self.c === 20);

// A special form based on the default operator proposal
const self_default_bound = -> (this ??= self, a, b) {
  this.c = a * b;
}
self_default_bound(6, 7);
assert(self.c === 42);

self_default_bound.call(other, 8, 9);
assert(other.c === 72);
assert(self.c === 42);

// ''=>'' is short for ''->'' with an explicit ''this'' parameter
function outer() {
  const bound    = => () this;
  const bound2   = -> (this = this) this; // initializer has outer
''this'' in scope
  const unbound  = -> () this;
  const unbound2 = -> (this) this;

  return [bound, bound2, unbound, unbound2];
}

const t = {},
      u = {};

const v = outer.call(t);

assert(v[0]() === t);
assert(v[1]() === t);
assert(v[2]() === t);
assert(v[3]() === t);

assert(v[0].call(u) === t);
assert(v[1].call(u) === t);
assert(v[2].call(u) === u);
assert(v[3].call(u) === u);

// Object intialiser shorthand: "method" = function-valued property
with dynamic ''this''
const obj = {
  method -> () {
    return => this;
  }
};

// Name binding forms hoist to body (var) or block (let, const) top
var -> warmer(a) {...};
let -> warm(b) {...};
const -> colder(c) {...};
const # -> coldest(d) {...};


Also, why not allow the explicit this parameter with current function
syntax as well via:

ArrowFunctionExpression :
    Arrow FormalParametersOpt InitialValue
    Arrow FormalParametersOpt BlockOpt

FunctionDeclaration :
    function Identifier FormalParameters { FunctionBody }

FunctionExpression :
    function IdentifierOpt FormalParameters { FunctionBody }

FormalParameters :
    ( FormalParameterListOpt )

FormalParameterList : ...
    this InitialiserOpt
    this InitialiserOpt , FormalParameterList


[1] http://wiki.ecmascript.org/doku.php?id=strawman:arrow_function_syntax


Thanks,
Sean Eagan


More information about the es-discuss mailing list