three small proposals: the bikeshed cometh!

Brendan Eich brendan at mozilla.com
Thu Apr 29 10:03:44 PDT 2010


On Apr 29, 2010, at 12:25 AM, Alex Russell wrote:

> Some small, pre-colored panels for the shed. Given that these are  
> mostly matters of syntax and not semantics, please believe me when I  
> suggest that the warts discussed herein present sharp edges that  
> should be rounded off by the committee -- not because they're  
> interesting in any way but because the primary users of the language  
> are shipping (bad) fixes around the network billions of times a day.  
> Such rank inefficiency needs sunset date.

This is not just about syntax. Let's ignore the threat of being  
accused of bikeshedding and evaluate holistically.


> Summary of proposals:
> ---------------------
>
> // 1.) Really generic generics
>
> ArrSubtype = function() { };
> ArrSubtype.prototype = Object.create(Array.prototype);
>
> var nas = new ArrSubtype();
> nas.push("howdy", "pardner");
>
> nas.slice(0) instanceof Array; // currently true
> nas.slice(0) instanceof ArrSubtype; // will be true after fix

An incompatible change, but would it break much code? Hard to say  
without trying it at scale. Here are some codesearch results:

http://www.google.com/codesearch?hl=en&lr=&q=%22prototype+%3D+[]%3B 
%22+lang%3Ajavascript&sbtn=Search
http://www.google.com/codesearch?hl=en&lr=&q=%22prototype+%3D+new+Array%3B%22+lang%3Ajavascript&sbtn=Search

How would it work exactly? More below on "subtyping".

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

The hash or number sign is not obviously all about functions. I've  
tried out the other alternatives in recent talks. No one is really  
enthusiastic about any of

λ foo() (bar + baz)
ƒ foo() (bar + baz)
\foo() (bar + baz)

(the foo name is optional but should be expressible).

The Greek lowercase lambda is actually an incompatible change from ES3  
(perfectly legal identifier there). It's also hard to type on most  
keyboards.

The florin is easier (alt-f on my Mac) but maybe a bit visually light  
and hard to scan for quickly, and it's arguably harder for newcomers  
to divine its meaning.

The \ does not make much sense, but it was proposed first among all of  
these on es-discuss, IIRC.

One wag replied after I had people vote on these "what about voting on  
function"? Many hands then went up, more than for any of the above.  
This set the cat among the shorthand-promoting pigeons.


> // 3.) Shorthand for Function.prototype.bind
>
> var boundMethod = obj!method;
> node.addEventListener("click", obj!method);
> node.removeEventListener("click", obj!method);

There's some precedent for ! as non-blocking send, returning a promise.


> NodeList.prototype.parents = function() {
>    // should return a NodeList
>     return this.map(function(n) { return n.parentNode; });
> }

IIRC a NodeList is a "live array", sort of a query-as-array or cursor  
that is continuously updated when the DOM mutates. It's really not an  
Array.


> * Kill code in libraries that exists only to wrap built-in methods  
> thanks to existing mis-specification of generics
> * Sub-types of Array suffer many warts, but making Array.prototype  
> methods return instances of subtypes will allow DOM-side changes to  
> make subtyping much more natural in real-world systems

"Subtype" is not well-defined in JS. Prototype-based delegation is not  
the <: relation from type theory, because of mutation, both of the  
prototype object and of the future binding of f in a scope and  
f.prototype in user-defined function f.

This isn't just a pedantic point. If we don't have a well-defined  
relation, how can we evaluate proposals that want to improve support  
for that relation, whatever it is?

In this case it seems to me you might want the result of Array  
generics to be created by calling (new this.constructor). For  
nas.slice(0), the generic slice code would then put elements got from | 
this| into the result of (new this.constructor). Is this the spec you  
want?

If so, it seems like an improvement, but again constructor has low  
integrity (none for user-defined constructor functions) without  
freeze, so there's no subtype relation in the formal <: sense. Still,  
it seems to me an improvement, ignoring the incompatibility.


>  // equivalent:
>  function(){ return 10; }
>  #(){ return 10; }
>  #{ return 10; } // no args, optionally elide ()

This does several things at once, and we have discussed one of them:  
turning the completion value of the body into the return value.

The objection to this, voiced clearly by Waldemar, is the unintended  
completion value in tail position leaking out as a return value. This  
is hard to see and test for, and it requires ugly void operator usage,  
or a dummy final value, to control.

Dave Herman has recently proposed http://wiki.ecmascript.org/doku.php?id=strawman:let_expressions 
, which include an explicit "completion value here" prefix: => 10; in  
tail position would result in 10, and without => the result would be  
the undefined value.


> * equivalent semantics to the function(){} syntax

No, not equivalent because tail position completion values are not  
return values with functions. Again semantics matter, not just syntax.


> 3.) Syntax for bound function de-reference
> [snip] So why does this suck? Two reasons: it's long-ish to type,  
> and it doesn't do what the dot operator does -- i.e., return the  
> same function object every time.

The lack of memoization is a good point. I've written at length about  
the challenges for implementations to "join" function objects as an  
optimization:

https://mail.mozilla.org/pipermail/es-discuss/2010-February/010830.html
https://mail.mozilla.org/pipermail/es-discuss/2010-February/010832.html


> What to do? We propose the bang operator -- ! -- as a new form of  
> binding de-reference of function objects. It's one character and  
> even includes a dot.  It has the following additional properties:
>
> 1.) all de-references via bang from an object/function pair return  
> the *same* function object (modulo #4).
> 4.) bang-bound functions are weakly referenced. If GC would  
> otherwise remove a function object from memory, having previously  
> bang-bound a function should not keep the function object alive


This is a tricky area. We have experience with such ephemeral objects.  
Problem is, sometimes users decorate them with ad-hoc properties  
("expandos"), which by your proposed rules will not keep them alive.  
The GC runs, and the decorator is surprised to see the ad-hoc  
properties gone.

Apart from the ! as promise-send and weak-vs.-expando issues, the big  
objection here is that you make each reference specify bound-ness and  
require memoization on the fly. An alternative would be to bind at  
method definition point. Then you could only extract the bound method,  
no matter how used, and via the conventional . operator. See

http://wiki.ecmascript.org/doku.php?id=strawman:obj_initialiser_methods

i'm against "invoke-only" methods, but bound method definition syntax  
is easier for users and implementors to swallow, and preserves dot as  
the one operator needed for extraction. It would also make the method  
reference strong, which would avoid the too-weak loss of expando  
problem.

Glad to see this proposed for discussion -- good suggestions,  
directionally and in some details.

/be
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20100429/b920e58d/attachment.html>


More information about the es-discuss mailing list