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