Operator overloading revisited

Brendan Eich brendan at mozilla.com
Fri Jul 3 04:20:24 PDT 2009

On Jul 3, 2009, at 1:21 AM, Christian Plesner Hansen wrote:

>> Likewise, for user-defined function foo, foo.prototype is writable  
>> -- but
>> not so for built-in constructor functions, and not so for classes  
>> as sugar
>> (more below).
> All JS code currently in existence is based on user-defined functions.
> For me that is the only case worth considering.  That might explain
> the differences in our views.

That's doesn't square with your proposal:

  function pointPlusNumber(a, b) {
    return new Point(a.x + b, a.y + b);

  Function.defineOperator('+', Point, Number, pointPlusNumber);

If you want to amend per Allen's suggestion by imposing fruitless,  
verbose ".prototype" taxes on the last line, so it reads

  Function.defineOperator('+', Point.prototype, Number.prototype,  

(or with some other argument order), then what good have you done for  
users? Unless Point and Number are frozen or otherwise configured in a  
non-default way, the next line could change one or both .prototype  
values and make later instances miss the operator.

Sure, that would be a case of "Doctor, it hurts when I do this" (so  
don't do it), but the same argument applies to instanceof, yet one  
writes (p instanceof Point), not (p instanceof Point.prototype).

You can't have it both ways. Either mutable .prototype (or mutable  
function binding, e.g. Point in the global object, or whatever scope  
it's bound in) is a problem that requires freezing or equivalent lock- 
down, or it isn't. In any event, there's no reason to impose  
the .prototype tax, and instanceof-consistency (evolutionary :-P)  
reason not to.

> I'm not a language revolutionary, I prefer gradual evolution.

Your political rhetoric is off target. Classes as sugar are not  
revolutionary, they build on evolutionary ES3 closures and ES5  
Object.freeze and similar features by adding syntactic sugar. They are  
mostly about "best practices" and "best usability" design defaults for  
higher-integrity factories than functions called via new can ever be  
by themselves (due to compatibility).

> If you have the option to use a model that works with the existing
> language and that integrates well even (that's subjective of course)
> why would you limit it to classes or values?

Because we're talking about value types, with that (typeof a == typeof  
b && a == b) <=> a === b implication extensible to user-defined value  
types, along with numeric literal syntax.

Value types are new "UI". The benefits won't accrue to user-defined  
functions as constructors without more opt-in API. And value types  
deserve first-class syntax, just as classes as sugar do. C# has  
structs, as Sam pointed out.

Without picking the exact syntax right now, the point remains that we  
have not been proposing only operators, or even only operators for  
user-defined constructor functions. We're talking about user-defined  
value types, ideally with concise, convenient defining as well as  
invoking syntax.

>  The reliance of these
> new features on each other will just lead them to become a
> language-within-a-language, rather than an evolution of the existing
> language.

There are lots of subsets in the language already, this is a teaching  
and user preference outcome that we can't and shouldn't stop. Multiple  
paradigms and well-ordered subsets are not a problem, they are a  
success condition.

But adding operators without considering first-class value types (==/ 
=== and literal syntax) looks like a mistake.

>> You could, but your proposal nicely avoided noisy ".prototype"  
>> expressions
>> all over, as Allen suggested in reply.
> That was a typo.

Why require .prototype noise when instanceof does not? Talk about  
evolution vs. revolution doesn't justify this break from instanceof's  
reliance on users to keep their constructor.prototype values intact --  
or not, as they please.

> If people can live with defining their methods as
> Foo.prototype.bar = function () { ... }

That's not the l33t way, of course:

Foo.prototype = {
     bar: function () {...}

But this is noisy and error-prone still, and you are wrong that what  
one "can live with" should be the last word. People "live with" all  
sorts of problems in the world, including in JS as it is, and  
certainly as it was before ES5 (which is definitely not the last word).

Classes as sugar proposes to sweeten such "method definitions", to  
lighten the boilerplate tax and reduce opportunities for nesting/ 
bracing/"this"-binding errors.

> I think they can live with writing .prototype when defining operators.

Your bikeshed is ugly and overlarge. :-P

> In fact I like the uniformity that you have to use the prototype in
> both cases.

No uniformity with instanceof, is that incipient revolution?

Perhaps just inconsistency for the sake of low-level explicitness, to  
be as fair as possible. Or possibly pedantry, but again: if user- 
defined constructor .prototype references are used and neither the  
constructor binding in its scope object nor the .prototype property  
are non-writable and non-configurable, then what is the point?

If the "type" name and .prototype *are* locked down (as with classes  
as sugar), then there's no good reason for .prototype, it's just a  
"syn-"tax and an unjustified inconsistency with respect to instanceof.

>  Note that library users won't see this, only the library
> implementer.  In any case, what users will "cry out for" is
> speculation.

We're talking about UI now, so user wishes count. And my bikeshed is  
prettier and smaller -- I invite others (Tucker can recuse himself ;-)  
to weigh in.

function "+"(a :Point, b :Number) {
   return new Point(a.x + b, a.y + b);

function "+"(a :Number, b :Point) {
   return new Point(a + b.x, a + b.y);

function "+"(a :Point, b :Point) {
   return new Point(a.x + b.x, a.y + b.y);

I'm not sold on Dylan's \+ (backslashes are troublesome and  
overloaded), but like quoting, it is a clean extension to the grammar.

>>> Whether or not A's '+' operator uses 'this', it's still a part of  
>>> A's
>>> behavior and the most consistent place to put it is where A's other
>>> behavior is, its prototype.
>> Could be, or one could use an optimized multimethod matrix, or some  
>> other
>> implementation approach. Again I do not see why the spec should  
>> dictate
>> observable prototype properties, internal or otherwise. If it just  
>> uses this
>> model but keeps it concealed, then I'm ok with it provided we don't  
>> close
>> the door on multimethods prematurely.
> JS allows introspection and self-modification and both are widely used
> and cited as advantages of the language.

Self-modification is widely criticized too, even in this thread. See  
Allen's measured post on Smalltalk history. Monkey-patching bites  
back, the ability to do it is a mixed blessing.

I chose to make most things mutable in Netscape 2 (even built-in  
constructor.prototype, if memory serves) because I had too little time  
to get the core language right, and I knew users would need to monkey- 
patch. That was then. As noted earlier in this thread, with ES5's  
Object.freeze etc., it will become harder over time to count on the  
ability to monkey-patch.

>  I would want to extend that
> to cover operators as well.

Why? I'm asking for specific use-cases, e.g. one-off or otherwise ad- 
hoc operator bindings.

You still haven't addressed the ==/=== relation and other "value type"  
issues such as literal notation extensibility.

> Having an internal property with some
> associated utility methods on Object is a nice way to get that.

The internal property is invisible to users of the language, and could  
be replaced by other implementation techniques. That's why I question  
it as a spec device.

You really did propose symmetric multimethods, but hooked them up  
"backstage of the language" using "this+" and "+this", etc. At this  
stage I contend we should focus on the fundamental design elements  
including multimethods, and not on the ES1-5-style internal property  
spec hacks.

And of course prettier, more usable bikesheds are fair game, if anyone  
wants to comment. Operators and other value type extensibility are  
motivated by usability problems with functions and methods and numeric  
literals jammed into strings.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20090703/059b074d/attachment-0001.html>

More information about the es-discuss mailing list