www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opXAssign overloading

reply dsimcha <dsimcha yahoo.com> writes:
It seems that D's operator overloading is a bit silly in some cases w.r.t.
opAddAssign, opSubAssign, etc.  Consider the following example:

struct Foo {
    Foo opAdd(Foo rhs) {
        return this;
    }
}

void main() {
    Foo foo;
    Foo bar;
    foo = foo + bar;  // Works.
    foo += bar;  // Doesn't work.
}

I'm thinking (not sure if this was proposed here before a while back and I
just forgot where I heard it from) that the default behavior of
someObject.opXAssign(otherStuff); should be to expand into someObject =
someObject.opX(otherStuff); if opXAssign is not overloaded.  Besides the
programmer being too lazy to explicitly overload opXAssign, I just found
another use case.

Suppose you have a bunch of classes and you're overloading operators such that
each call builds another layer of decorators.  For example:

class SomeClass {
    SomeClass opAdd(SomeClass rhs) {
       // SomeDecorator is a subclass of SomeClass.
       return new SomeDecorator(this, rhs);
    }
}

In this case, you *can't* overload SomeClass.opAddAssign in any reasonable way
because you can't assign SomeDecorator to this, but translating
someInstance.opAddAssign(someOtherInstance) into someInstance =
someInstance.opAdd(someOtherInstance) makes perfect sense.
Oct 12 2009
parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 13 Oct 2009 00:31:32 -0400, dsimcha <dsimcha yahoo.com> wrote:

 It seems that D's operator overloading is a bit silly in some cases  
 w.r.t.
 opAddAssign, opSubAssign, etc.  Consider the following example:

 struct Foo {
     Foo opAdd(Foo rhs) {
         return this;
     }
 }

 void main() {
     Foo foo;
     Foo bar;
     foo = foo + bar;  // Works.
     foo += bar;  // Doesn't work.
 }

 I'm thinking (not sure if this was proposed here before a while back and  
 I
 just forgot where I heard it from) that the default behavior of
 someObject.opXAssign(otherStuff); should be to expand into someObject =
 someObject.opX(otherStuff); if opXAssign is not overloaded.  Besides the
 programmer being too lazy to explicitly overload opXAssign, I just found
 another use case.

 Suppose you have a bunch of classes and you're overloading operators  
 such that
 each call builds another layer of decorators.  For example:

 class SomeClass {
     SomeClass opAdd(SomeClass rhs) {
        // SomeDecorator is a subclass of SomeClass.
        return new SomeDecorator(this, rhs);
     }
 }

 In this case, you *can't* overload SomeClass.opAddAssign in any  
 reasonable way
 because you can't assign SomeDecorator to this, but translating
 someInstance.opAddAssign(someOtherInstance) into someInstance =
 someInstance.opAdd(someOtherInstance) makes perfect sense.
Also, if you template both opX and opX_r you will always get a overload conflict.
Oct 12 2009
parent reply Don <nospam nospam.com> writes:
Robert Jacques wrote:
 On Tue, 13 Oct 2009 00:31:32 -0400, dsimcha <dsimcha yahoo.com> wrote:
 
 It seems that D's operator overloading is a bit silly in some cases 
 w.r.t.
 opAddAssign, opSubAssign, etc.  Consider the following example:

 struct Foo {
     Foo opAdd(Foo rhs) {
         return this;
     }
 }

 void main() {
     Foo foo;
     Foo bar;
     foo = foo + bar;  // Works.
     foo += bar;  // Doesn't work.
 }

 I'm thinking (not sure if this was proposed here before a while back 
 and I
 just forgot where I heard it from) that the default behavior of
 someObject.opXAssign(otherStuff); should be to expand into someObject =
 someObject.opX(otherStuff); if opXAssign is not overloaded.  Besides the
 programmer being too lazy to explicitly overload opXAssign, I just found
 another use case.

 Suppose you have a bunch of classes and you're overloading operators 
 such that
 each call builds another layer of decorators.  For example:

 class SomeClass {
     SomeClass opAdd(SomeClass rhs) {
        // SomeDecorator is a subclass of SomeClass.
        return new SomeDecorator(this, rhs);
     }
 }

 In this case, you *can't* overload SomeClass.opAddAssign in any 
 reasonable way
 because you can't assign SomeDecorator to this, but translating
 someInstance.opAddAssign(someOtherInstance) into someInstance =
 someInstance.opAdd(someOtherInstance) makes perfect sense.
 Also, if you template both opX and opX_r you will always get a overload 
 conflict.
Yes, I posted a patch for that to the ng. It's very simple. There has been some discussion as to whether arithmetic operator overloading makes sense at all for reference types. My feeling is that if x = x + y; has a different meaning to x += y; then operator overloading doesn't make sense. But, perhaps with the decorator idea you are close to having a use case for classes where operator overloading makes sense?
Oct 13 2009
parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Oct 13, 2009 at 2:30 AM, Don <nospam nospam.com> wrote:
 Robert Jacques wrote:
 On Tue, 13 Oct 2009 00:31:32 -0400, dsimcha <dsimcha yahoo.com> wrote:

 It seems that D's operator overloading is a bit silly in some cases
 w.r.t.
 opAddAssign, opSubAssign, etc. =A0Consider the following example:

 struct Foo {
 =A0 =A0Foo opAdd(Foo rhs) {
 =A0 =A0 =A0 =A0return this;
 =A0 =A0}
 }

 void main() {
 =A0 =A0Foo foo;
 =A0 =A0Foo bar;
 =A0 =A0foo =3D foo + bar; =A0// Works.
 =A0 =A0foo +=3D bar; =A0// Doesn't work.
 }

 I'm thinking (not sure if this was proposed here before a while back an=
d
 I
 just forgot where I heard it from) that the default behavior of
 someObject.opXAssign(otherStuff); should be to expand into someObject =
=3D
 someObject.opX(otherStuff); if opXAssign is not overloaded. =A0Besides =
the
 programmer being too lazy to explicitly overload opXAssign, I just foun=
d
 another use case.

 Suppose you have a bunch of classes and you're overloading operators su=
ch
 that
 each call builds another layer of decorators. =A0For example:

 class SomeClass {
 =A0 =A0SomeClass opAdd(SomeClass rhs) {
 =A0 =A0 =A0 // SomeDecorator is a subclass of SomeClass.
 =A0 =A0 =A0 return new SomeDecorator(this, rhs);
 =A0 =A0}
 }

 In this case, you *can't* overload SomeClass.opAddAssign in any
 reasonable way
 because you can't assign SomeDecorator to this, but translating
 someInstance.opAddAssign(someOtherInstance) into someInstance =3D
 someInstance.opAdd(someOtherInstance) makes perfect sense.
 Also, if you template both opX and opX_r you will always get a overload
 conflict.
Yes, I posted a patch for that to the ng. It's very simple. There has been some discussion as to whether arithmetic operator overload=
ing
 makes sense at all for reference types. My feeling is that if =A0x =3D x =
+ y;
 has a different meaning to x +=3D y; then operator overloading doesn't ma=
ke
 sense.

 But, perhaps with the decorator idea you are close to having a use case f=
or
 classes where operator overloading makes sense?
Well, I have implemented an N-d array-like type as a class before. I don't think it's the best way to do it, but someone might like to have a general array class they could override to behave as a matrix. There is such a thing in Numpy. I don't think it's the greatest idea in the world, but if you do make such a thing, then you want to be able to make +=3D identity preserving and =3D not. And that's how stuff works in Python in general. +=3D modifies an object, =3D changes its identity. So I wouldn't say it's nonsense to have such a rule. What you can say is that this better be true (to within numerical precision) x1 =3D x + y x +=3D y =3D=3D> x1 =3D=3D x --bb x+
Oct 13 2009