suggestion: mapping symbolic infix ops to binary functions

Brendan Eich brendan at mozilla.org
Sat Jul 21 15:25:51 PDT 2012


We've already been looking at value types, proxies, and objects for a 
while. The latest, 
http://wiki.ecmascript.org/doku.php?id=strawman:value_objects, does not 
expose user-defined operators, to separate concerns and limit scope, but 
user operators are doable.

Just for the record, not to say experiments aren't helpful: we aren't 
going to use mangled names starting with dot, or clump user-defined 
operators at left-associative low precedence. Those are anti-goals, really.

The prototype in SpiderMonkey of int64 and uint64 value objects is 
tracked by

https://bugzilla.mozilla.org/show_bug.cgi?id=749786

(I need to put up a rebased patch -- will do early this coming week).

The dispatch mechanism for dyadic operators is due to Christian Plesner 
Hansen, as I mentioned here:

https://brendaneich.com/2012/06/recent-talks-fluent-txjs-2012/

Look for UPDATE:. (Still awaiting the txjs 2012 video.)

The dispatch mechanism for dyadic operators is equivalent to 
multimethods, but cacheable in modern JITs, and modular in the sense 
that double-dispatch is not. This was proposed by Christian Plesner 
Hansen here in 2009:

https://mail.mozilla.org/pipermail/es-discuss/2009-June/009603.html

 From the patch for bug 749786:

    When executing the '+' operator in 'A + B' where A and B refer to value
    objects, do the following:

     1. Get the value of property LOP_PLUS in A, call the result P
     2. If P is not a list, throw a TypeError: no '+' operator
     3. Get the value of property ROP_PLUS in B, call the result Q
     4. If Q is not a list, throw a TypeError: no '+' operator
     5. Intersect the lists P and Q, call the result R
     6. If R is empty throw, a TypeError: no '+' operator
     7. If R has more than one element, throw a TypeError: ambiguity
     8. If R[0], call it F, is not a function, throw a TypeError
     9. Evaluate F(A, B) and return the result

    Rather than use JS-observable identifiers to label operator 
handedness as
    in Christian's proposal ('this+', '+this'), we use SpecialId 
variants that
    cannot be named in-language or observed by proxies.

    To support operator overloading in-language, we need only provide an API
    similar to the one Christian proposed:

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

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

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

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

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

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

I plan to prototype the Function.defineOperator API too, after first 
writing it up as a strawman and discussing it with TC39ers.

/be

Claus Reinke wrote:
> Hi again,
>
> my thread starter email seems to have disappeared (see below), perhaps 
> this reply will get through. The topic is still user-defined
> infix operations: in the short term, to experiment with proposed 
> language extensions, in the long term, to shift this topic from tc39
> to library authors.
>
> I've now created a gist with an implementation sketch. It uses a 
> modified esprima to parse infix operations, translating to Operator-
> applications on the fly, then uses escodegen to generate source from 
> the AST, and optionally evaluates the result (using a little node 
> wrapper):
>
>    map symbolic infix ops '(x .<op> y)' to 'Operation[".<op>"](x,y)'
>    https://gist.github.com/3157251
>
> Missing features include precedence and associativity (in this sketch, 
> all infix ops are left-associative between 'logical or' and 
> 'conditional expression'; also, all infix ops start with a '.', 
> followed by a sequence of '.<>=!+-*%&|^/~?:#'). Still, what is left is 
> sufficient to play with the examples, as shown in this sample source file
>
>    https://gist.github.com/3157251#file_sample.js
>
>    $ git clone git://gist.github.com/3157251.git
>    $ cd 3157251/
>    $ node infix.js sample.js --eval
>    hi
>    my name is obj
>    undefined
>    { x: 1, y: 0 }
>
> Hope you like it,
> Claus
>
> --------------------------------------------------
> From: "Claus Reinke" <..>
> Sent: Wednesday, July 11, 2012 10:01 PM
> To: <es-discuss at ..>
> Subject: suggestion: mapping symbolic infix ops to binary functions
>
>> The discussion of re-interpreting arrows in method contexts
>> reminded me of an older idea: if we had user-defined infix
>> operators in JS, then such features could be field-tested easily,
>> without involving tc39.
>>
>> ---------
>> Basic idea (in a full spec, this would have to be fitted in the 
>> existing binary operator precedence stack) - syntax:
>>    SymbolicOperator :
>>        any sequence of symbolic chars not valid in ES5
>>
>>    BinaryOperatorExpression :
>>        Expression SymbolicOperator Expression
>>
>> Semantics: assuming a single predefined 'Operator' object, extensible 
>> with function properties for each symbolic infix operator, the phrase
>>
>>    e1 <op> e2
>>
>> would be rewritten (desugared) to
>>
>>    Operator["<op>"](e1,e2)
>>
>> ---------
>> Example uses:
>>
>> 1. to play with Irakli's idea, we could define
>>
>>    Operator["#"] =        function(obj,arr) { return arr(obj) };
>>
>> to get a version of object property selection that provides
>> its function argument with a this reference, so that
>>
>>    { x: "hi" } # (obj => obj.x)    // would return "hi"
>>
>> 2. to play with a simple variant of '<|', we could define
>>
>>    Operator['<|'] =        function(proto,obj){ obj.__proto__ = 
>> proto; return obj }
>>
>> so that
>>
>>    proto <| obj    // would set obj's __proto__ property
>>
>> 3. to get a version of object property selection that does
>> not fail over when subpaths don't exist, we could define
>>
>>    Operator['.?'] =        function(obj,prop){ return  ( obj || {} 
>> )[prop] }
>>
>> so that
>>
>>    (undefined .? "a") .? "b"    // would return undefined
>>
>> [note that this example does not have an object as left operand,
>> hence the use of 'Operator' in operator desugaring]
>>
>> 4. to get a chainable version of object extension, we could define
>>
>>    Operator['##'] =
>>        function(obj,ext) {            
>> Object.keys(ext).forEach(function(k) { obj[k]=ext[k] });
>>            return obj
>>        }
>>
>> so that
>>
>>    ({} ## { x: 1 }) ## { y: 0 }     // would return { x: 1, y: 0}
>>
>> and so on..
>>
>> Any takers?-)
>>
>> Claus
>> http://clausreinke.github.com/
>>
>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>


More information about the es-discuss mailing list