Operator overloading revisited

Christian Plesner Hansen christian.plesner.hansen at gmail.com
Sun Jun 28 07:05:22 PDT 2009


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?


-- Christian


More information about the es-discuss mailing list