Operator overloading revisited

Brendan Eich brendan at mozilla.com
Wed Jul 1 07:23:08 PDT 2009


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

>> Almost. It's actually a bit worse than that, since presumaby,  
>> Points want to
>> be addable with Numbers:
>>
>> Point.prototype['+'] = function (that) {
>>   if (that instanceof Point) {
>>    return new Point(this.x + that.x, this.y + that.y);
>>  } else if (that instanceof Number) {
>>    // again ignoring the difference between numbers and Numbers
>>    return new Point(this.x + that, this.y + that);
>>  } else {
>>    return that['reverse+'](this);
>>  }
>> };

BTW, Mark's code reminds me of a paper on tracing double-dispatch I've  
mentioned in past talks:

http://www.ics.uci.edu/~franz/Site/pubs-pdf/ICS-TR-07-10.pdf

The approach of double-dispatching on what you know and bottoming out  
in a reverse-foo call when you don't know the other operand's type to  
me looks like an application "when in doubt, use brute  
force" (Thompson).

That can be an effective strategy, provided you're truly in doubt  
about better ways to solve the problem. But it is verbose and probably  
error prone. I much prefer something like your

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

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

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

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

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

  Function.defineOperator('+', Point, Point, addPoints);

to the if/else mess cited above (plus the reverse+ other half).

How much sweeter could this be sugared?

  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);
  }

(Here the quoted operator names imply multimethod addition, where  
previously I used |generic function| to Tucker's enthusiastic +∞).

A programming language should not necessarily require low-level coding  
from its users just to conserve dispatch mechanism. Users are the  
masters, we specifiers and implementors are the servants.

Yes, adding a novel dispatch mechanism adds complexity and cognitive  
load for those learning the whole language. No, not everyone learns  
the whole language at once. Complexity can be contained if the new  
dispatch machinery is well-separated and provided it composes well.  
Done right, it's not going to burn any n00bs.

Sure, we could leave out multimethods and use double dispatch. We  
could leave out operators altogether and make 'em eat functions! But  
the motivation here is usability, not theoretical computability.


> I find the use of instanceof problematic for several reasons.

Yeah, that's why I used "types" in scare-quotes. But for now can we  
defer the interface or behavior ("type" as a four-letter word) debate,  
and try to agree on multimethods vs. double dispatch?


>> Perhaps Complex(3, 5) === Complex(3, 5) is a better example?
>
> I really don't see it.  What is the problem with two complex numbers
> being distinct object instances and strict equals reflecting that
> fact?  If you want structural equality use ==, that's what it's there
> for.

No, because for two numbers a and b, a == b <=> a === b. In fact for  
any two values, (typeof a == typeof b && a == b) <=> a === b. If we  
want to support usable numeric and similar "scalar" type extensions,  
we might want to preserve this logic.

/be


More information about the es-discuss mailing list