www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Operator Overloading - elegant and easy to compile

reply Tomasz Sowinski <tomeksowi gmail.com> writes:
NOTE: I put a more readible version of this post on Zoho:
http://writer.zoho.com/public/tomeksowi/Operator-Overloading


Yet another try to get operator overloading right.

The reason I'm revisiting this issue is that most users are not particularily
proud of the way operator overloading is handled in D. The foreach statement,
array slicing and concatenation as well as many other features hit the jackpot.
Operator overloading did not.

Consider this example:

class Foo
{
    int field;

    this (int a)  { field = a; }

    Foo op(+, -, *, /)(Foo value)
    {
        return  new Foo(this.field op value.field);
    }

    void op(+=, -=, *=, /=)(Foo value)
    {
        this.field op value.field;
    }

    bool op(<, <=, >, >=, ==, !=)(Foo value)
    {
        return (this.field op value.field);
    }

    Foo op(++op, --op)()
    {
        op(this.field);
        return this;
    }

    Foo op(op++, op--)()
    {
        Foo temp = new Foo(this.field);
        op(this.field);
        return temp;
    }
}

You probably have already guessed what this proposal is about by reading the
example, but I'm going to explain it anyway:

The idea is to treat operator overloading like a function template. Of course,
the above examples are not valid function templates, still it would be good to
have an op declaration that would feel like one. For example, if the compiler
sees someting like this:
    Foo op(+, -, *, /)(Foo value) {...}
It would make operators opAdd, opSub, opMul, opDiv and all the op..._r's as
well.

To generalize, the declaration would be:

OperatorDeclaration:
ReturnType op (OperatorSymbolList) (ParameterList) OperatorBlockStatement

The compiler then generates appropriate functions by iterating through
OperatorSymbolList and substituting every occurence of the op keyword in the
OperatorBlockStatement with an operator symbol from the OperatorSymbolList and
then compiling it as any other code.

The reason of having such an approach is that it is very often that operator
overloading is done by applying the operator you would apply to a custom type
to each (some of) the custom type's fields.

Some pros of this proposal:

    * it complies so well with the 'don't write things twice' rule
    * elegant syntax - no need to learn all those opQueerNames by heart
    * no problem with parsing (no special case needed to prevent from
understanding "operator<(...)" as "operator is less than (...)" like in C++)
    * is natural, and melts into existing syntax (if one is familiar with
function templates, he will easily understand the declaration)
    * performance gains of easy replacing opCmp with a set of specific
comparison functions

I haven't thought of cons yet, as I just came up with this idea and can't help
thinking it's brilliant :) If you think otherwise, discuss, show me how wrong I
am.

(btw, it's my first post, so big Hello to everyone)


Tomek
Apr 14 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Tomasz Sowinski wrote:
 NOTE: I put a more readible version of this post on Zoho:
 http://writer.zoho.com/public/tomeksowi/Operator-Overloading
 
 
 Yet another try to get operator overloading right.
 
 The reason I'm revisiting this issue is that most users are not particularily
proud of the way operator overloading is handled in D. The foreach statement,
array slicing and concatenation as well as many other features hit the jackpot.
Operator overloading did not.
 
 Consider this example:
 
 class Foo
 {
     int field;
 
     this (int a)  { field = a; }
 
     Foo op(+, -, *, /)(Foo value)
     {
         return  new Foo(this.field op value.field);
     }
 
     void op(+=, -=, *=, /=)(Foo value)
     {
         this.field op value.field;
     }
 
     bool op(<, <=, >, >=, ==, !=)(Foo value)
     {
         return (this.field op value.field);
     }
 
     Foo op(++op, --op)()
     {
         op(this.field);
         return this;
     }
 
     Foo op(op++, op--)()
     {
         Foo temp = new Foo(this.field);
         op(this.field);
         return temp;
     }
 }
 
 You probably have already guessed what this proposal is about by reading the
example, but I'm going to explain it anyway:
 
 The idea is to treat operator overloading like a function template. Of course,
the above examples are not valid function templates, still it would be good to
have an op declaration that would feel like one. For example, if the compiler
sees someting like this:
     Foo op(+, -, *, /)(Foo value) {...}
 It would make operators opAdd, opSub, opMul, opDiv and all the op..._r's as
well.
 
 To generalize, the declaration would be:
 
 OperatorDeclaration:
 ReturnType op (OperatorSymbolList) (ParameterList) OperatorBlockStatement
 
 The compiler then generates appropriate functions by iterating through
OperatorSymbolList and substituting every occurence of the op keyword in the
OperatorBlockStatement with an operator symbol from the OperatorSymbolList and
then compiling it as any other code.
 
 The reason of having such an approach is that it is very often that operator
overloading is done by applying the operator you would apply to a custom type
to each (some of) the custom type's fields.
 
 Some pros of this proposal:
 
     * it complies so well with the 'don't write things twice' rule
     * elegant syntax - no need to learn all those opQueerNames by heart
     * no problem with parsing (no special case needed to prevent from
understanding "operator<(...)" as "operator is less than (...)" like in C++)
     * is natural, and melts into existing syntax (if one is familiar with
function templates, he will easily understand the declaration)
     * performance gains of easy replacing opCmp with a set of specific
comparison functions
 
 I haven't thought of cons yet, as I just came up with this idea and can't help
thinking it's brilliant :) If you think otherwise, discuss, show me how wrong I
am.
 
 (btw, it's my first post, so big Hello to everyone)
Howdy! Can you be more specific on how to deal with the _r variants of operator overloads? The first con that comes to my mind is * Sometimes you want to explicitly call an overloaded operator by name, or get a delegate for it to pass to another function. Also Walter doesn't like treating the operator symbols as pure symbols. Part of the reason he gave the D operator overloads names like "opAdd" was to discourage people from trying to overload "+" to mean anything other than addition. So your proposal will face an uphill battle because of that. Macros also might be able to provide this kind of thing. Someday, when they exist, that is. --bb
Apr 14 2008
next sibling parent Tomasz Sowinski <tomeksowi gmail.com> writes:
Bill Baxter Wrote:

 Can you be more specific on how to deal with the _r variants of operator 
 overloads?
I never thought of explicit definitions of _r operators, point taken. But it can easily be amended: Foo op_reverse(+, -, *, /)(Foo value) {...} the op_reverse keyword resembles the existing foreach_reverse. In the BlockStatement the keyword would be still "op" as it's shorter and melts into code more naturally.
 The first con that comes to my mind is
 * Sometimes you want to explicitly call an overloaded operator by name, 
 or get a delegate for it to pass to another function.
I never wanted the new syntax to replace the old one, but to amend it. The old one would be used like explicit memory management - rare, but available if one needs.
 Also Walter doesn't like treating the operator symbols as pure symbols. 
   Part of the reason he gave the D operator overloads names like "opAdd" 
 was to discourage people from trying to overload "+" to mean anything 
 other than addition.  So your proposal will face an uphill battle 
 because of that.
Frankly, I think in the current system nothing but common sense stops you from writing: int opAdd(int i) { int targetsHit = 0; while (--i > 0) targetsHit += LaunchNuclearMissle; return targetsHit; } And the new proposal does, because it replaces "op" with an op symbol from the list and it can be *nothing else* but that symbol. If you want several ops overloaded at a time, it's hard to get the overloads to mean anything else than the original ops on a type's fields. So I think it's a good way to turn programmers' laziness into code that follows recommended guidelines:)
 Macros also might be able to provide this kind of thing.  Someday, when 
 they exist, that is.
Can you point me to a description of macros implementation-to-be in D? Can't find them on the DM website. Tomek
Apr 15 2008
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Bill Baxter, el 15 de abril a las 08:50 me escribiste:
 Also Walter doesn't like treating the operator symbols as pure symbols.  Part
of the 
 reason he gave the D operator overloads names like "opAdd" was to discourage
people from 
 trying to overload "+" to mean anything other than addition.  So your proposal
will face 
 an uphill battle because of that.
I think Walter forgot that when he added opStar ;) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Yo soy peperino el que siempre pone el vino, yo soy aquel que come los huevos con tocino. -- Peperino PĆ³moro
Apr 15 2008
next sibling parent Tomasz Sowinski <tomeksowi gmail.com> writes:
Leandro Lucarella Wrote:

 I think Walter forgot that when he added opStar ;)
I've always wondered why this isn't called opValueOf or something :)
Apr 15 2008
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Leandro Lucarella wrote:
 Bill Baxter, el 15 de abril a las 08:50 me escribiste:
 Also Walter doesn't like treating the operator symbols as pure symbols.  Part
of the 
 reason he gave the D operator overloads names like "opAdd" was to discourage
people from 
 trying to overload "+" to mean anything other than addition.  So your proposal
will face 
 an uphill battle because of that.
I think Walter forgot that when he added opStar ;)
Yeh, I think there was pretty much a consensus on the NG that it should be called opDeref. --bb
Apr 15 2008