www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Comparing apples and oranges

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Consider:

class Apple {}
class Orange {}

void main() {
     writeln(new Apple == new Orange);
}

This program always prints "false".  By and large, it is odd that one 
would attempt comparison between unrelated classes. I was thinking, is 
this ever legitimate, or we should just disallow it statically whenever 
possible?

The comparison would still remain possible by casting to a parent class:

     writeln(cast(Object) new Apple == cast(Object) new Orange);

I could think of rare cases in which one would want two sibling types to 
compare equal. Consider:

class Matrix { ... }
// No state added, operations optimized with BLAS
class BLASMatrix : Matrix {}
// No state added, operations optimized with LAPACK
class LAPACKMatrix : Matrix {}

Since neither derived class adds any state, both act in comparisons just 
like the base class Matrix, so it's valid to compare a BLASMatrix with a 
LAPACKMatrix.

How do you think we should go about this? Stay error-prone for the 
benefit of a few cases, or disallow sibling class comparisons statically?


Andrei
Sep 29 2009
next sibling parent Jeremie Pelletier <jeremiep gmail.com> writes:
Andrei Alexandrescu wrote:
 Consider:
 
 class Apple {}
 class Orange {}
 
 void main() {
     writeln(new Apple == new Orange);
 }
 
 This program always prints "false".  By and large, it is odd that one 
 would attempt comparison between unrelated classes. I was thinking, is 
 this ever legitimate, or we should just disallow it statically whenever 
 possible?
 
 The comparison would still remain possible by casting to a parent class:
 
     writeln(cast(Object) new Apple == cast(Object) new Orange);
 
 I could think of rare cases in which one would want two sibling types to 
 compare equal. Consider:
 
 class Matrix { ... }
 // No state added, operations optimized with BLAS
 class BLASMatrix : Matrix {}
 // No state added, operations optimized with LAPACK
 class LAPACKMatrix : Matrix {}
 
 Since neither derived class adds any state, both act in comparisons just 
 like the base class Matrix, so it's valid to compare a BLASMatrix with a 
 LAPACKMatrix.
 
 How do you think we should go about this? Stay error-prone for the 
 benefit of a few cases, or disallow sibling class comparisons statically?
 
 
 Andrei
 

You can already explicitly do "(new Apple).opEquals(new Orange);" so why not first resolve == to opEquals and then try to match the parameters, walking through all known opEquals until a matching one is found.
Sep 29 2009
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Consider:
 
 class Apple {}
 class Orange {}
 
 void main() {
     writeln(new Apple == new Orange);
 }
 
 This program always prints "false".  By and large, it is odd that one 
 would attempt comparison between unrelated classes. I was thinking, is 
 this ever legitimate, or we should just disallow it statically whenever 
 possible?

Has this brought problems to anyone ever?
Sep 29 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Consider:

 class Apple {}
 class Orange {}

 void main() {
     writeln(new Apple == new Orange);
 }

 This program always prints "false".  By and large, it is odd that one 
 would attempt comparison between unrelated classes. I was thinking, is 
 this ever legitimate, or we should just disallow it statically 
 whenever possible?

Has this brought problems to anyone ever?

I'm not sure about this particular aspect, but in Java the related method equals() has been a perennial source of issues. Just google for java equals. Andrei
Sep 29 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 29 Sep 2009 18:54:28 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Consider:

 class Apple {}
 class Orange {}

 void main() {
      writeln(new Apple == new Orange);
 }

 This program always prints "false".  By and large, it is odd that one  
 would attempt comparison between unrelated classes. I was thinking, is  
 this ever legitimate, or we should just disallow it statically whenever  
 possible?

 The comparison would still remain possible by casting to a parent class:

      writeln(cast(Object) new Apple == cast(Object) new Orange);

 I could think of rare cases in which one would want two sibling types to  
 compare equal. Consider:

 class Matrix { ... }
 // No state added, operations optimized with BLAS
 class BLASMatrix : Matrix {}
 // No state added, operations optimized with LAPACK
 class LAPACKMatrix : Matrix {}

 Since neither derived class adds any state, both act in comparisons just  
 like the base class Matrix, so it's valid to compare a BLASMatrix with a  
 LAPACKMatrix.

 How do you think we should go about this? Stay error-prone for the  
 benefit of a few cases, or disallow sibling class comparisons statically?


 Andrei

I believe Java and C# took bool Object.equals(Object other); way because they lacked generics intially and stored all the instances as Objects in containers (having equals method in Object allowed them proper ordering etc). D doesn't suffer from that problem and doesn't have to follow the same way those languages took. BTW, nowadays, they define IComparable<T> interface, which is a recommended way to implement comparison functions. That's why I'm all for removing opEquals from Object.
Sep 29 2009
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Denis Koroskin:

 BTW, nowadays, they define IComparable<T> interface, which is a  
 recommended way to implement comparison functions.
 
 That's why I'm all for removing opEquals from Object.

I think that asks for some extra optimizations that I think currently D front-end doesn't perform. Bye, bearophile
Sep 29 2009
prev sibling next sibling parent reply Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Tue, Sep 29, 2009 at 11:17 AM, Denis Koroskin <2korden gmail.com> wrote:
 I believe Java and C# took bool Object.equals(Object other); way because
 they lacked generics intially and stored all the instances as Objects in
 containers (having equals method in Object allowed them proper ordering
 etc).

 D doesn't suffer from that problem and doesn't have to follow the same way
 those languages took.

I agree, opEquals and opCmp in Object feel like out-of-date relics. It's particularly annoying how, if you want your classes to behave with arrays/AAs, you must override opEquals(Object), meaning you have to do silly downcasts at runtime.
 BTW, nowadays, they define IComparable<T> interface, which is a recommended
 way to implement comparison functions.

I think D, with its much better generic support, could basically use duck typing and achieve the same effects without the penalty of an interface.
Sep 29 2009
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Jarrett Billingsley wrote:
 On Tue, Sep 29, 2009 at 11:17 AM, Denis Koroskin <2korden gmail.com> wrote:
 I believe Java and C# took bool Object.equals(Object other); way because
 they lacked generics intially and stored all the instances as Objects in
 containers (having equals method in Object allowed them proper ordering
 etc).


I believe Java still does internally, and generics are just a coding convenience. http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
 D doesn't suffer from that problem and doesn't have to follow the same way
 those languages took.

I agree, opEquals and opCmp in Object feel like out-of-date relics. It's particularly annoying how, if you want your classes to behave with arrays/AAs, you must override opEquals(Object), meaning you have to do silly downcasts at runtime.

I've always been against opCmp being in Object. opEquals being in Object is harder to dismiss, as I can imagine that someone might find a use case for being able to store an arbitrary mix of objects in a container and in doing so be able to compare them. But I do wonder if Object.opEquals could work better. Maybe make it so that, if a class C contains a method opEquals(C), and opEquals(Object) has not been explicitly overridden by the programmer anywhere up the hierarchy, then opEquals(Object) will be automagically overridden to call opEquals(C). The only trouble is that I'm not sure how well this would work when interfaces are involved.
 BTW, nowadays, they define IComparable<T> interface, which is a recommended
 way to implement comparison functions.


Both Java's Comparable and, AFAICMO, .NET's IComparable, are for ordering comparisons. Equality comparison is built into Object in both cases. Stewart.
Sep 29 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Tue, 29 Sep 2009 18:54:28 +0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Consider:

 class Apple {}
 class Orange {}

 void main() {
      writeln(new Apple == new Orange);
 }

 This program always prints "false".  By and large, it is odd that one 
 would attempt comparison between unrelated classes. I was thinking, is 
 this ever legitimate, or we should just disallow it statically 
 whenever possible?

 The comparison would still remain possible by casting to a parent class:

      writeln(cast(Object) new Apple == cast(Object) new Orange);

 I could think of rare cases in which one would want two sibling types 
 to compare equal. Consider:

 class Matrix { ... }
 // No state added, operations optimized with BLAS
 class BLASMatrix : Matrix {}
 // No state added, operations optimized with LAPACK
 class LAPACKMatrix : Matrix {}

 Since neither derived class adds any state, both act in comparisons 
 just like the base class Matrix, so it's valid to compare a BLASMatrix 
 with a LAPACKMatrix.

 How do you think we should go about this? Stay error-prone for the 
 benefit of a few cases, or disallow sibling class comparisons statically?


 Andrei

I believe Java and C# took bool Object.equals(Object other); way because they lacked generics intially and stored all the instances as Objects in containers (having equals method in Object allowed them proper ordering etc). D doesn't suffer from that problem and doesn't have to follow the same way those languages took. BTW, nowadays, they define IComparable<T> interface, which is a recommended way to implement comparison functions. That's why I'm all for removing opEquals from Object.

What would you replace it with? Note that IComparable<T> does not quite solve a lot of the problems, at least does not make things much easier for the programmer. Comparing objects is really a double dispatch problem. Andrei
Sep 29 2009
parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 29/09/2009 18:13, Andrei Alexandrescu wrote:
 Denis Koroskin wrote:
 On Tue, 29 Sep 2009 18:54:28 +0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Consider:

 class Apple {}
 class Orange {}

 void main() {
 writeln(new Apple == new Orange);
 }

 This program always prints "false". By and large, it is odd that one
 would attempt comparison between unrelated classes. I was thinking,
 is this ever legitimate, or we should just disallow it statically
 whenever possible?

 The comparison would still remain possible by casting to a parent class:

 writeln(cast(Object) new Apple == cast(Object) new Orange);

 I could think of rare cases in which one would want two sibling types
 to compare equal. Consider:

 class Matrix { ... }
 // No state added, operations optimized with BLAS
 class BLASMatrix : Matrix {}
 // No state added, operations optimized with LAPACK
 class LAPACKMatrix : Matrix {}

 Since neither derived class adds any state, both act in comparisons
 just like the base class Matrix, so it's valid to compare a
 BLASMatrix with a LAPACKMatrix.

 How do you think we should go about this? Stay error-prone for the
 benefit of a few cases, or disallow sibling class comparisons
 statically?


 Andrei

I believe Java and C# took bool Object.equals(Object other); way because they lacked generics intially and stored all the instances as Objects in containers (having equals method in Object allowed them proper ordering etc). D doesn't suffer from that problem and doesn't have to follow the same way those languages took. BTW, nowadays, they define IComparable<T> interface, which is a recommended way to implement comparison functions. That's why I'm all for removing opEquals from Object.

What would you replace it with? Note that IComparable<T> does not quite solve a lot of the problems, at least does not make things much easier for the programmer. Comparing objects is really a double dispatch problem. Andrei

How about implementing multiple-dispatch than?
Sep 29 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Yigal Chripun wrote:
 On 29/09/2009 18:13, Andrei Alexandrescu wrote:
 Denis Koroskin wrote:
 On Tue, 29 Sep 2009 18:54:28 +0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Consider:

 class Apple {}
 class Orange {}

 void main() {
 writeln(new Apple == new Orange);
 }

 This program always prints "false". By and large, it is odd that one
 would attempt comparison between unrelated classes. I was thinking,
 is this ever legitimate, or we should just disallow it statically
 whenever possible?

 The comparison would still remain possible by casting to a parent 
 class:

 writeln(cast(Object) new Apple == cast(Object) new Orange);

 I could think of rare cases in which one would want two sibling types
 to compare equal. Consider:

 class Matrix { ... }
 // No state added, operations optimized with BLAS
 class BLASMatrix : Matrix {}
 // No state added, operations optimized with LAPACK
 class LAPACKMatrix : Matrix {}

 Since neither derived class adds any state, both act in comparisons
 just like the base class Matrix, so it's valid to compare a
 BLASMatrix with a LAPACKMatrix.

 How do you think we should go about this? Stay error-prone for the
 benefit of a few cases, or disallow sibling class comparisons
 statically?


 Andrei

I believe Java and C# took bool Object.equals(Object other); way because they lacked generics intially and stored all the instances as Objects in containers (having equals method in Object allowed them proper ordering etc). D doesn't suffer from that problem and doesn't have to follow the same way those languages took. BTW, nowadays, they define IComparable<T> interface, which is a recommended way to implement comparison functions. That's why I'm all for removing opEquals from Object.

What would you replace it with? Note that IComparable<T> does not quite solve a lot of the problems, at least does not make things much easier for the programmer. Comparing objects is really a double dispatch problem. Andrei

How about implementing multiple-dispatch than?

Yah, how about it? :o) Andrei
Sep 29 2009