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
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss