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