Arrow binding

Alex Russell alex at dojotoolkit.org
Mon Apr 23 03:53:20 PDT 2012


Despite making repeated arguments for "soft binding", I'm pretty sure I haven't outlined here what it actually would *be*. Now that we're looking to add syntactic forms that create bound function objects (arrows and class methods), perhaps it's time to get consensus for or against. Soft binding has 2 properties that make it desirable:

  * The global contract that methods can have their "this" re-set with .call() and .apply() is maintained
  * Common-case usage avoids the hazards of unbound and mis-appropriated "this" contexts. Most commonly, passing a method to a function which takes a callback:

     node.addEventListener("click", foo.bar);

The language today has 2 types of functions:

   * unbound: methods for which "this" is not fixed
   * hard-bound: methods bound by Function.prototype.bind()

Crucially, we have no syntax which creates hard-bound methods which means that they're not common (yet). To the extent that they are used, it is explicitly through forms like:

     node.addEventListener("click", foo.bar.bind(foo));

Or through libraries:

     dojo.connect(node, "onclick", foo, "bar");

This means that most users of most functions can still use .call() and .apply() without apprehension. Functions are still "just functions".

The new forms we're adding (methods and arrows) have the potential to change this radically, causing a large percentage of functions encountered by programmers to have binding. If that binding is hard-binding, .call() and .apply() break in the minds of users. Perhaps that's fine by you, but in addition to being a contractual failure, it removes a form of genericness which is unique in the language.

What to do?

One option is to barrel onward with either unbound functions, hard bound functions, or some mix thereof. These are all painful in ways I don't need to spend time here explaining. I propose a third alternative: soft binding (aka "preferred binding"). It enables the following:

     node.addEventListener("click", foo.bar.prefer(foo));

While still allowing the following:

     foo.bar.call(otherThis, …args);

Functions with preferred bindings can still be re-bound either with new preferred binding or with new hard binding (both forms vend new functions objects and they do today).

Here's a JSFiddle with an a quick ES5 desugaring + example:

      http://jsfiddle.net/slightlyoff/739CS/20/

Note that we need to re-define .call() and .apply() to be savvy to preferences, but this doesn't seem particularly painful. I've bluntly worked around it in this example to avoid __proto__ re-wiring.

Thoughts?

--
Alex Russell
slightlyoff at google.com
slightlyoff at chromium.org
alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723



More information about the es-discuss mailing list