digitalmars.D.learn - opCmp, opEquals
- bearophile (8/8) Oct 23 2008 I have a small question regarding OOP design. The signature of opCmp, op...
- Jarrett Billingsley (2/10) Oct 23 2008 I think C2 is the right one as well.
- Steven Schveighoffer (19/40) Oct 23 2008 Given that the default implementation just compares references, I think ...
- bearophile (86/90) Oct 23 2008 In that C1 class I have left out the implementation of opCmp, let's say ...
- Ary Borenszweig (12/91) Oct 23 2008 If you are comparing something to something else that you shouldn't be
- Carl Clark (5/105) Oct 23 2008 I don't think assert is quite what you'd want here; it seems like an
- Steven Schveighoffer (12/32) Oct 23 2008 Yes, but consistently bad ;)
- bearophile (18/28) Oct 23 2008 I see, thank you for the idea.
-
Stewart Gordon
(10/16)
Oct 28 2008
I have a small question regarding OOP design. The signature of opCmp, opEquals when defined inside classes require a Object argument. So you have to define what to do when the given object can't be cast to the this class. Here you can see two alternative designs: http://codepad.org/tOHnTObl I think the C1 design is a bad design and the C2 is the right one, but I'd like to know your opinion. I think Python 2.6+ uses the C1 design, Python3+ uses the C2 design. Someone told me that Java uses a mixed approach: the opEquals returns false when the cast is not possible, while the cmp throws an exception. Bye, bearophile
Oct 23 2008
On Thu, Oct 23, 2008 at 11:04 AM, bearophile <bearophileHUGS lycos.com> wrote:I have a small question regarding OOP design. The signature of opCmp, opEquals when defined inside classes require a Object argument. So you have to define what to do when the given object can't be cast to the this class. Here you can see two alternative designs: http://codepad.org/tOHnTObl I think the C1 design is a bad design and the C2 is the right one, but I'd like to know your opinion. I think Python 2.6+ uses the C1 design, Python3+ uses the C2 design. Someone told me that Java uses a mixed approach: the opEquals returns false when the cast is not possible, while the cmp throws an exception. Bye, bearophileI think C2 is the right one as well.
Oct 23 2008
"Jarrett Billingsley" wroteOn Thu, Oct 23, 2008 at 11:04 AM, bearophile <bearophileHUGS lycos.com> wrote:Given that the default implementation just compares references, I think C1 is correct. Otherwise you have weird shit like this: class C3 { // no opEquals defined, very common } auto c3 = new C3; auto c2 = new C2(0); if(c3 == c2) // fails, but does not throw { foo(); } if (c2 == c3) // throws exception { foo(); } -SteveI have a small question regarding OOP design. The signature of opCmp, opEquals when defined inside classes require a Object argument. So you have to define what to do when the given object can't be cast to the this class. Here you can see two alternative designs: http://codepad.org/tOHnTObl I think the C1 design is a bad design and the C2 is the right one, but I'd like to know your opinion. I think Python 2.6+ uses the C1 design, Python3+ uses the C2 design. Someone told me that Java uses a mixed approach: the opEquals returns false when the cast is not possible, while the cmp throws an exception. Bye, bearophileI think C2 is the right one as well.
Oct 23 2008
Steven Schveighoffer:Given that the default implementation just compares references, I think C1 is correct. Otherwise you have weird shit like this: [...something bad]In that C1 class I have left out the implementation of opCmp, let's say I use the following one (C4a and C4b have the same code, but they are distinct classes): class UncomparableException: Exception { this() { super(this.classinfo.name); } this(string msg) { super(this.classinfo.name ~ ": " ~ msg); } } class C4a { // Like Java? int x; this(int x) { this.x = x; } int opEquals(Object o) { if (this is o) return 1; auto oc = cast(typeof(this))o; if (oc is null) return 0; else return this.x == oc.x; } int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; if (oc is null) throw new UncomparableException(); else return this.x - oc.x; } } class C4b { // Like Java? int x; this(int x) { this.x = x; } int opEquals(Object o) { if (this is o) return 1; auto oc = cast(typeof(this))o; if (oc is null) return 0; else return this.x == oc.x; } int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; if (oc is null) throw new UncomparableException(); else return this.x - oc.x; } } void main() { auto x = new C4a(1); auto y = new C4b(2); printf(x == y ? "x == y\n" : "x != y\n"); // else printf(x.opCmp(y) == 0 ? "x ==2 y\n" : "x !=2 y\n"); // throws } I think that's more bad behavior. But then what can I do? remove the UncomparableException from opCmp too, and make it return randomly -1 or +1 when the classes can't be cast, like this? class C5 { // ? int x; this(int x) { this.x = x; } int opEquals(Object o) { if (this is o) return 1; auto oc = cast(typeof(this))o; if (oc is null) return 0; else return this.x == oc.x; } int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; if (oc is null) return 1; // random value != 0 else return this.x - oc.x; } } Bye, bearophile
Oct 23 2008
bearophile wrote:Steven Schveighoffer:If you are comparing something to something else that you shouldn't be comparing, an exception is the best solution. It will fail fast, and show you that there is an error in your application code. Another altenative, more in the D style, it to place an assert: int opCmp(Object o) { if (this is o) return 0; assert(typeof(o) is typeof(this)); auto oc = cast(typeof(this))o; return this.x - oc.x; }Given that the default implementation just compares references, I think C1 is correct. Otherwise you have weird shit like this: [...something bad]In that C1 class I have left out the implementation of opCmp, let's say I use the following one (C4a and C4b have the same code, but they are distinct classes): class UncomparableException: Exception { this() { super(this.classinfo.name); } this(string msg) { super(this.classinfo.name ~ ": " ~ msg); } } class C4a { // Like Java? int x; this(int x) { this.x = x; } int opEquals(Object o) { if (this is o) return 1; auto oc = cast(typeof(this))o; if (oc is null) return 0; else return this.x == oc.x; } int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; if (oc is null) throw new UncomparableException(); else return this.x - oc.x; } } class C4b { // Like Java? int x; this(int x) { this.x = x; } int opEquals(Object o) { if (this is o) return 1; auto oc = cast(typeof(this))o; if (oc is null) return 0; else return this.x == oc.x; } int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; if (oc is null) throw new UncomparableException(); else return this.x - oc.x; } } void main() { auto x = new C4a(1); auto y = new C4b(2); printf(x == y ? "x == y\n" : "x != y\n"); // else printf(x.opCmp(y) == 0 ? "x ==2 y\n" : "x !=2 y\n"); // throws } I think that's more bad behavior. But then what can I do? remove the UncomparableException from opCmp too, and make it return randomly -1 or +1 when the classes can't be cast, like this?
Oct 23 2008
On 2008-10-23 11:42, Ary Borenszweig wrote:bearophile wrote:I don't think assert is quite what you'd want here; it seems like an exception is really better, since asserts are more for invariant conditions -- the function doesn't know that no one will ever call it with bad arguments, so it shouldn't be an assert.Steven Schveighoffer:If you are comparing something to something else that you shouldn't be comparing, an exception is the best solution. It will fail fast, and show you that there is an error in your application code. Another altenative, more in the D style, it to place an assert: int opCmp(Object o) { if (this is o) return 0; assert(typeof(o) is typeof(this)); auto oc = cast(typeof(this))o; return this.x - oc.x; }Given that the default implementation just compares references, I think C1 is correct. Otherwise you have weird shit like this: [...something bad]In that C1 class I have left out the implementation of opCmp, let's say I use the following one (C4a and C4b have the same code, but they are distinct classes): class UncomparableException: Exception { this() { super(this.classinfo.name); } this(string msg) { super(this.classinfo.name ~ ": " ~ msg); } } class C4a { // Like Java? int x; this(int x) { this.x = x; } int opEquals(Object o) { if (this is o) return 1; auto oc = cast(typeof(this))o; if (oc is null) return 0; else return this.x == oc.x; } int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; if (oc is null) throw new UncomparableException(); else return this.x - oc.x; } } class C4b { // Like Java? int x; this(int x) { this.x = x; } int opEquals(Object o) { if (this is o) return 1; auto oc = cast(typeof(this))o; if (oc is null) return 0; else return this.x == oc.x; } int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; if (oc is null) throw new UncomparableException(); else return this.x - oc.x; } } void main() { auto x = new C4a(1); auto y = new C4b(2); printf(x == y ? "x == y\n" : "x != y\n"); // else printf(x.opCmp(y) == 0 ? "x ==2 y\n" : "x !=2 y\n"); // throws } I think that's more bad behavior. But then what can I do? remove the UncomparableException from opCmp too, and make it return randomly -1 or +1 when the classes can't be cast, like this?
Oct 23 2008
"bearophile" wroteSteven Schveighoffer:Given that the default implementation just compares references, I think C1 is correct. Otherwise you have weird shit like this: [...something bad]In that C1 class I have left out the implementation of opCmp, let's say I use the following one (C4a and C4b have the same code, but they are distinct classes): [snip]void main() { auto x = new C4a(1); auto y = new C4b(2); printf(x == y ? "x == y\n" : "x != y\n"); // else printf(x.opCmp(y) == 0 ? "x ==2 y\n" : "x !=2 y\n"); // throws } I think that's more bad behavior.Yes, but consistently bad ;) that is, x.opCmp(y) results in the same thing (throwing an exception) as someObj.opCmp(y), where someObj's opCmp is the default implementation (note that this is currently broken in Tango, but not in druntime). The real solution here is to have opCmp not be in object, but in an interface. The fact that the default opCmp throws an exception is a huge clue to this. But I doubt that's changing anytime soon.But then what can I do? remove the UncomparableException from opCmp too, and make it return randomly -1 or +1 when the classes can't be cast, like this?The exception is the default behavior, so it should be what you use for the default case. -Steve
Oct 23 2008
Steven Schveighoffer:that is, x.opCmp(y) results in the same thing (throwing an exception) as someObj.opCmp(y), where someObj's opCmp is the default implementation [...]< The exception is the default behavior, so it should be what you use for the default case.<Okay. D works like Java here, it seems. So I'll do the same then, thank you.The real solution here is to have opCmp not be in object, but in an interface. The fact that the default opCmp throws an exception is a huge clue to this.<I see, thank you for the idea. If you think this is the best solution, then I think that now with the refinment of druntime is the best moment to change things. Later this detail of D2 will be too much frozen to change. So Sean may be asked to express an opinion about this. --------------------- Ary Borenszweig:int opCmp(Object o) { if (this is o) return 0; assert(typeof(o) is typeof(this)); auto oc = cast(typeof(this))o; return this.x - oc.x; }I like something like this better: int opCmp(Object o) { if (this is o) return 0; auto oc = cast(typeof(this))o; assert(oc !is null); return this.x - oc.x; } But in the end I'll keep the runtime exception in my opCmp methods. Thank to everyone that has given me a clue, bye, bearophile
Oct 23 2008
"bearophile" <bearophileHUGS lycos.com> wrote in message news:gdq3pq$126t$1 digitalmars.com...I have a small question regarding OOP design. The signature of opCmp, opEquals when defined inside classes require a Object argument. So you have to define what to do when the given object can't be cast to the this class. Here you can see two alternative designs:<snip>Someone told me that Java uses a mixed approach: the opEquals returns false when the cast is not possible, while the cmp throws an exception.This seems sensible to me. If two objects are of completely different classes, obviously they aren't equal to each other. But there's no means by which one can be picked out as being 'less than' the other. Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Oct 28 2008