three small proposals: the bikeshed cometh!

Tom Van Cutsem tomvc at google.com
Thu Apr 29 10:42:20 PDT 2010


Allow me to make the case that harmony proxies can ease some of your
(syntactic) pains:


>  // 2.) Shorthand for "function", aka the "not lambda"
>
>  node.addEventListener("click", #(e){ e.preventDefault(); });
>  node.removeEventListener("click" obj!#(e){ ... }); // see #3
>

If what you're looking for is simply a convenient shorthand, Harmony proxies
may partially satisfy your needs.
See: <
http://wiki.ecmascript.org/doku.php?id=harmony:proxies#higher-order_messages
>

Basically, the expression _.foo(x) returns a function(receiver) {
receiver.foo(x) } such that your first example would become:

node.addEventListener("click", _.preventDefault() );

 // makes for easy-to-read operations:
>  [ ... ].map(#(i){ i+10; });
>

Ah, if only '+' were a message, then you'd be able to write:
[ ... ].map( _.+(10) )

Nevertheless: the pattern array.map(function (e) { e.someMessage() }) should
be common enough such that it can benefit from the '_' shorthand.


> 3.) Syntax for bound function de-reference
>

Using a similar trick as above + MarkM's proposed Ephemeron
Tables<http://wiki.ecmascript.org/doku.php?id=strawman:ephemeron_tables>to
enable memory-leak-free caching of bound functions, a small adaptation
of
your function results in:

// a map of obj -> (fun -> boundfun)
var _bound = new EphemeronTable();

var bang = function(obj) {
  return Proxy.create({
    get: function(_bang, func) {
      var fn = obj[func];
      // Still no isFunction...*sigh*
      if (Object.prototype.toString.call(fn) === "[object Function]") {
        var objBoundFuns = _bound.get(obj);
        if (objBoundFuns && objBoundFuns.get(fn)) {
          return objBoundFuns.get(fn); // bound function already existed
        } else {
          // create a new bound function
          var bound = fn.bind(obj);
          if (objBoundFuns) {
            // obj already has bound functions, add it to the existing map
            objBoundFuns.set(fn, bound);
          } else {
            // create a new mapping from obj -> bound functions of obj
            objBoundFuns = new EphemeronTable();
            objBoundFuns.set(fn, bound);
            _bound.set(obj, objBoundFuns);
          }
          return bound; // return new bound fun
        }
      } else {
        // not a function object, just return the deref
        return fn;
      }
    }
  })
};

 node.addEventListener("click", obj!method);
>  // actually removed without extra book-keeping or wrappers!
>  node.removeEventListener("click", obj!method);
>  [...]
>  // ES3 impl lacks nice syntax:
>  node.addEventListener("click", bang(obj, "method"));
>  // works!
>  node.removeEventListener("click", bang(obj, "method"));
>

Now consider (I renamed 'bang' to 'bind', as it seems more sensible):

node.addEventListener("click", bind(obj).method );
node.removeEventListener("click", bind(obj).method );

Granted, the overhead compared to dedicated syntax is probably significant.
Then again, the fact that this pattern avoids dedicated syntax while
remaining concise is also significant :-)

Cheers,
Tom
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20100429/d85dc148/attachment-0001.html>


More information about the es-discuss mailing list