Thread overview
Operator Overloading - elegant and easy to compile
Apr 14, 2008
Tomasz Sowinski
Apr 14, 2008
Bill Baxter
Apr 15, 2008
Tomasz Sowinski
Apr 15, 2008
Leandro Lucarella
Apr 15, 2008
Tomasz Sowinski
Apr 15, 2008
Bill Baxter
April 14, 2008
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




April 14, 2008
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
April 15, 2008
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

April 15, 2008
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
April 15, 2008
Leandro Lucarella Wrote:

> I think Walter forgot that when he added opStar ;)

I've always wondered why this isn't called opValueOf or something :)
April 15, 2008
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