Operator overloading revisited

Christian Plesner Hansen christian.plesner.hansen at gmail.com
Thu Jul 2 03:35:22 PDT 2009

I think we could get away with making an exception there.  The spec
algorithm for how to execute '+' would check if the operands were
primitive numbers before attempting user-defined operators.

There are already cases where changes to Number.prototype don't
propagate to primitive numbers, leading them to behave differently.
For instance:

Number.prototype.valueOf = function () { return 4; };
print(1 + 1); // yields 2
print(1 + new Number(1)); // yields 5

On Thu, Jul 2, 2009 at 11:53 AM, Maciej Stachowiak<mjs at apple.com> wrote:
> On Jun 28, 2009, at 7:05 AM, Christian Plesner Hansen wrote:
>> Following the decimal discussion where values came up I looked for
>> information on that.  The most substantial I could find was Mark's
>> operator overloading strawman.
>> I think the current strawman has a number of weaknesses.  It relies on
>> the (hypothetical) type system which makes it fundamentally different
>> from normal method dispatch.  It uses double dispatch for all
>> user-defined operators which makes optimization difficult.  It is
>> asymmetrical: the left hand side always controls dispatch.
>> I'd like to propose an alternative approach that avoids these
>> problems.  You could call it "symmetric" operator overloading.  When
>> executing the '+' operator in 'a + b' you do the following (ignoring
>> inheritance for now):
>> 1: Look up the property 'this+' in a, call the result 'oa'
>> 2: If oa is not a list give an error, no '+' operator
>> 3: Look up the property '+this' in b, call the result ob
>> 4: If ob is not a list give an error, no '+' operator
>> 5: Intersect the lists oa and ob, call the result r
>> 6: If r is empty give an error, no '+' operator
>> 7: If r has more than one element give an error, ambiguity
>> 8: If r[0], call it f, is not a function give an error
>> 9: Otherwise, evaluate f(a, b) and return the result.
>> The way to define a new operator is to add the same function to the
>> 'this+' list of the left-hand side and the '+this' list on the
>> right-hand side.  This would probably be handled best by a simply
>> utility function, say Function.defineOperator:
>>  function pointPlusNumber(a, b) {
>>   return new Point(a.x + b, a.y + b);
>>  }
>>  Function.defineOperator('+', Point, Number, pointPlusNumber);
>> Using this approach it's easy to extend symmetrically:
>>  function numberPlusPoint(a, b) {
>>   return new Point(a + b.x, a + b.y);
>>  }
>>  Function.defineOperator('+', Number, Point, numberPlusPoint);
>> In this case you need two different functions, one for Point+Number
>> and one for Number+Point, but in many cases the same function can be
>> used for both:
>>  function addPoints(a, b) {
>>   return new Point(a.x + b.x, a.y + b.y);
>>  }
>>  Function.defineOperator('+', Point, Point, addPoints);
>> This approach is completely symmetric in the two operands.  Operator
>> dispatch is fairly similar to ordinary method dispatch and doesn't
>> rely on a type system.  Programmers don't have to manually implement
>> double dispatch.  It my be a bit more work to do lookup but on the
>> other hand it is straightforward to apply inline caching so often
>> you'll only need to do that work once.  It can be extended to deal
>> with inheritance -- you might consider all operators up through the
>> scope chain and pick the most specific one based on how far up the
>> scope chain you had to look.
>> It may look a bit foreign but I think it has a lot of nice properties.
>> Comments?
> It seems like once you do the defineOperator calls you described above, and
> assuming they work for primitive numbers and not just Number objects, then
> adding two numbers would require two property lookups, unless there is an
> exception for operating on two primitive values.
> Regards,
> Maciej

More information about the es-discuss mailing list