Jump to page: 1 26  
Page
Thread overview
Operator overloading through UFCS doesn't work
Oct 13, 2012
Tommi
Oct 13, 2012
Jakob Ovrum
Oct 13, 2012
Jonathan M Davis
Oct 13, 2012
Tommi
Oct 13, 2012
Jonathan M Davis
Oct 13, 2012
Tommi
Oct 13, 2012
Maxim Fomin
Oct 13, 2012
Tommi
Oct 13, 2012
Maxim Fomin
Oct 13, 2012
Tommi
Oct 13, 2012
Tommi
Oct 13, 2012
Jonathan M Davis
Oct 13, 2012
Timon Gehr
Oct 13, 2012
H. S. Teoh
Oct 13, 2012
Timon Gehr
May 24, 2016
Elie Morisse
May 24, 2016
Jonathan M Davis
May 25, 2016
Elie Morisse
May 25, 2016
Jonathan M Davis
May 25, 2016
Elie Morisse
May 26, 2016
Jonathan M Davis
May 26, 2016
Elie Morisse
May 26, 2016
Jonathan M Davis
May 27, 2016
Marc Schütz
May 29, 2016
Jonathan M Davis
May 30, 2016
Marc Schütz
May 30, 2016
pineapple
May 31, 2016
ixid
May 31, 2016
Jonathan M Davis
May 26, 2016
Timon Gehr
Oct 14, 2012
Maxim Fomin
Oct 14, 2012
Artur Skawina
Oct 15, 2012
Maxim Fomin
Oct 15, 2012
Artur Skawina
Oct 15, 2012
Maxim Fomin
Oct 16, 2012
Artur Skawina
Oct 16, 2012
Maxim Fomin
Oct 17, 2012
Artur Skawina
Oct 17, 2012
Maxim Fomin
Oct 17, 2012
Timon Gehr
Oct 17, 2012
Maxim Fomin
Oct 17, 2012
Timon Gehr
Oct 17, 2012
Timon Gehr
Oct 17, 2012
Artur Skawina
Oct 14, 2012
Maxim Fomin
Oct 14, 2012
Tommi
Oct 14, 2012
Maxim Fomin
Oct 14, 2012
Tommi
Oct 15, 2012
Maxim Fomin
Oct 16, 2012
Tommi
Oct 16, 2012
Maxim Fomin
Oct 17, 2012
Timon Gehr
Oct 17, 2012
Maxim Fomin
Oct 17, 2012
Timon Gehr
Oct 13, 2012
Timon Gehr
Oct 14, 2012
Maxim Fomin
Oct 17, 2012
Timon Gehr
October 13, 2012
Quote from TDPL: "D’s approach to operator overloading is simple: whenever at least one participant in an operator expression is of user-defined type, the compiler rewrites the expression into a regular method call with a specific name. Then the regular language rules apply."

According to the above, I think the following code should work:

struct MyStruct
{
    int _value;
}

ref MyStruct opUnary(string op : "++")(ref MyStruct ms)
{
    ++ms._value;
    return ms;
}

MyStruct opBinary(string op : "+")(MyStruct ms, int value)
{
    return MyStruct(ms._value + value);
}

void main()
{
    MyStruct ms;

    ms.opUnary!"++"();                 // #1: OK
    MyStruct ms2 = ms.opBinary!"+"(1); // #2: OK

    ++ms;                  // #3
    MyStruct ms3 = ms + 1; // #4
}

#3: Error: 'ms += 1' is not a scalar, it is a MyStruct

#4: Error: incompatible types for ((ms) + (1)):
'MyStruct' and 'int'


I'd expect the lines tagged #3 and #4 to be rewritten by the compiler like so:
ms.opUnary!"++"();
MyStruct ms3 = ms.opBinary!"+"(1);

So, the inability to do operator overloading though UFCS must be a compiler bug, right?
October 13, 2012
On Saturday, 13 October 2012 at 08:36:19 UTC, Tommi wrote:
> Quote from TDPL: "D’s approach to operator overloading is simple: whenever at least one participant in an operator expression is of user-defined type, the compiler rewrites the expression into a regular method call with a specific name. Then the regular language rules apply."

Do note that this says *method* call. Your example doesn't use methods. Hence, the current state of operator overloading is consistent with TDPL.

October 13, 2012
On Saturday, October 13, 2012 11:06:27 Jakob Ovrum wrote:
> On Saturday, 13 October 2012 at 08:36:19 UTC, Tommi wrote:
> > Quote from TDPL: "D’s approach to operator overloading is simple: whenever at least one participant in an operator expression is of user-defined type, the compiler rewrites the expression into a regular method call with a specific name. Then the regular language rules apply."
> 
> Do note that this says *method* call. Your example doesn't use methods. Hence, the current state of operator overloading is consistent with TDPL.

Yes. It is most definitely illegal to overload any operators as free functions. They're _always_ member variables of the type that they operate on.

- Jonathan M Davis
October 13, 2012
On Saturday, 13 October 2012 at 09:06:28 UTC, Jakob Ovrum wrote:
> Do note that this says *method* call. Your example doesn't use methods. Hence, the current state of operator overloading is consistent with TDPL.

I don't agree with the last sentence. According to TDPL:

1) "whenever at least one participant in an operator expression
is of user-defined type, the compiler rewrites the expression
into a regular method call with a specific name"
---------------------------------------------------------------
++var;
gets rewritten to:
var.opUnary!"++"();

2) "if a.fun(b, c, d) is seen but fun is not a member of a’s
type, D rewrites that as fun(a, b, c, d) and tries that as well"
----------------------------------------------------------------
So, because opUnary is not a member of var, compiler should rewrite that as:
.opUnary!"++"(var);
October 13, 2012
On Saturday, October 13, 2012 11:41:07 Tommi wrote:
> On Saturday, 13 October 2012 at 09:06:28 UTC, Jakob Ovrum wrote:
> > Do note that this says *method* call. Your example doesn't use methods. Hence, the current state of operator overloading is consistent with TDPL.
> 
> I don't agree with the last sentence. According to TDPL:
> 
> 1) "whenever at least one participant in an operator expression is of user-defined type, the compiler rewrites the expression into a regular method call with a specific name"
> ---------------------------------------------------------------
> ++var;
> gets rewritten to:
> var.opUnary!"++"();
> 
> 2) "if a.fun(b, c, d) is seen but fun is not a member of a’s
> type, D rewrites that as fun(a, b, c, d) and tries that as well"
> ----------------------------------------------------------------
> So, because opUnary is not a member of var, compiler should
> rewrite that as:
> .opUnary!"++"(var);

Just because the overloaded operator is rewritten into a method call underneath the hood doesn't mean that the UFCS rewrite also applies. The transformation of an operator to a method or a UFCS call to a free function is done _once_. You don't get both. It is most definitely _by design_ that you cannot overload operators except as member functions. If TDPL says otherwise, it's either because you're misunderstanding what it's saying or because it's wrong.

- Jonathan M Davis
October 13, 2012
On Saturday, 13 October 2012 at 09:50:05 UTC, Jonathan M Davis wrote:
> It is most definitely _by design_ that you cannot
> overload operators except as member functions.

I don't understand this design choice then. I don't see any problem in allowing UFCS operators. Because of the way UFCS works, it's guaranteed that there can't be any operator hijacking.
October 13, 2012
On Saturday, 13 October 2012 at 10:00:22 UTC, Tommi wrote:
> On Saturday, 13 October 2012 at 09:50:05 UTC, Jonathan M Davis wrote:
>> It is most definitely _by design_ that you cannot
>> overload operators except as member functions.
>
> I don't understand this design choice then. I don't see any problem in allowing UFCS operators. Because of the way UFCS works, it's guaranteed that there can't be any operator hijacking.

I think implementing UFCS operator overloading is problematic. Firstly, you want to put this language addition too far. Secondly, compiler needs to know whether operator was overloaded or not. If it knows, it generates code to call "opSomething", if nor - it just doesn't generate anything. Now, imagine what would happen if you write in some module "free" function, supposed to hijack operator overloading method of class or struct in another module. If you compile them together, operator would be overloaded, if separately - nothing would happen. This means that operator overloading would depend on with what you compile your module - sometimes nothing would be overloaded, sometimes it would be with one function, sometimes with another. Thirdly, I see no reason in allowing it - for what purpose does you proposal service for?
October 13, 2012
On Saturday, 13 October 2012 at 11:50:40 UTC, Maxim Fomin wrote:
> I think implementing UFCS operator overloading is problematic. Firstly, you want to put this language addition too far.

I don't see this as taking UFCS functionality "further". Rather, I think it's simply more logical that with UFCS you could provide extra operator methods just like you can provide extra "regular" methods. I assumed that UFCS operator overloading would work for sure, and it seems arbitrary to me that it doesn't.


> Secondly, compiler needs to know whether operator was overloaded or not. If it knows, it generates code to call "opSomething", if nor - it just doesn't generate anything. Now, imagine what would happen if you write in some module "free" function, supposed to hijack operator overloading method of class or struct in another module. If you compile them together, operator would be overloaded, if separately - nothing would happen. This means that operator overloading would depend on with what you compile your module - sometimes nothing would be overloaded, sometimes it would be with one function, sometimes with another.

You use the word "hijack", but free functions can't hijack anything. They can only provide new functionality. The situation you describe is exactly parallel to using UFCS (with regular functions) like this:

//File: mystruct.d
module mystruct;

struct MyStruct
{
    int _value;
}

//File: incr1.d
module incr1;

import mystruct;

void incr(ref MyStruct ms)
{
    ms._value += 1;
}

//File: incr2.d
module incr2;

import mystruct;

void incr(ref MyStruct ms)
{
    ms._value += 2;
}

//File: main.d
module main;

import std.stdio;
import mystruct;

static if (true) // change to false to print "2"
    import incr1;
else
    import incr2;

void main()
{
    MyStruct ms;
    ms.incr();
    writeln(ms._value); // prints "1"
}


> Thirdly, I see no reason in allowing it - for what purpose does you proposal service for?

The main reason to me is that it would make more sense. It'd seem more logical that way. I can't think of any use cases.
October 13, 2012
On Saturday, 13 October 2012 at 15:06:14 UTC, Tommi wrote:
> On Saturday, 13 October 2012 at 11:50:40 UTC, Maxim Fomin wrote:
>> I think implementing UFCS operator overloading is problematic. Firstly, you want to put this language addition too far.
>
> I don't see this as taking UFCS functionality "further". Rather, I think it's simply more logical that with UFCS you could provide extra operator methods just like you can provide extra "regular" methods. I assumed that UFCS operator overloading would work for sure, and it seems arbitrary to me that it doesn't.

I don't consider request to put the language in consistency with superficial logic (UFCS allows to use free functions as methods => allow operator overloading hijacking too) as a rational one.

>> Secondly, compiler needs to know whether operator was overloaded or not. If it knows, it generates code to call "opSomething", if nor - it just doesn't generate anything. Now, imagine what would happen if you write in some module "free" function, supposed to hijack operator overloading method of class or struct in another module. If you compile them together, operator would be overloaded, if separately - nothing would happen. This means that operator overloading would depend on with what you compile your module - sometimes nothing would be overloaded, sometimes it would be with one function, sometimes with another.
>
> You use the word "hijack", but free functions can't hijack anything.

Their inability to hijack actual methods is not an issue.

> They can only provide new functionality. The situation you describe is exactly parallel to using UFCS (with regular functions) like this:
>
> //File: mystruct.d
> module mystruct;
>
> struct MyStruct
> {
>     int _value;
> }
>
> //File: incr1.d
> module incr1;
>
> import mystruct;
>
> void incr(ref MyStruct ms)
> {
>     ms._value += 1;
> }
>
> //File: incr2.d
> module incr2;
>
> import mystruct;
>
> void incr(ref MyStruct ms)
> {
>     ms._value += 2;
> }
>
> //File: main.d
> module main;
>
> import std.stdio;
> import mystruct;
>
> static if (true) // change to false to print "2"
>     import incr1;
> else
>     import incr2;
>
> void main()
> {
>     MyStruct ms;
>     ms.incr();
>     writeln(ms._value); // prints "1"
> }
>
>

Yes. Do you want to have this with operator overloading too? UFCS is useful if you import some library and want to add extra functionality to particular needs of a module. So, if you do this multiple times and merge modules you may not get into trouble with high probability of conflicting names. Even if they conflict, you can slightly rename them. Can you do this if you define in several modules different operator overloading methods? Guess not.

>> Thirdly, I see no reason in allowing it - for what purpose does you proposal service for?
>
> The main reason to me is that it would make more sense. It'd seem more logical that way. I can't think of any use cases.

Different groups of people have different mind and same things produce different sense on them. From my point of view operator overloading methods are special functions and not treating them as candidates for UFCS does make more sense. Even if you convince in your opinion, language addition without applied purposes makes no benefit.
October 13, 2012
On Saturday, 13 October 2012 at 16:02:25 UTC, Maxim Fomin wrote:
> From my point of view operator overloading methods are
> special functions and not treating them as candidates for
> UFCS does make more sense.

I can think of only one thing that makes custom operator methods "special" or different from regular methods. It's the fact that you don't *have* to call them through the normal method invocation syntax: var.opSomething(...), but rather, the language provides this nice layer of syntactic sugar through which you *can* call those methods, if you so choose to.

What you're saying is, that calling those operator methods through this layer of syntactic sugar, e.g. var + 3, is somehow fundamentally different from directly calling the method, to which this layer of syntactic sugar would forward the expression to call anyway, i.e. var.opBinary!"+"(3)
« First   ‹ Prev
1 2 3 4 5 6