Thread overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
July 23, 2014 WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
This morning, I discovered this major WAT in D: ---- struct S { int x; int y; int opCmp(S s) { return x - s.x; // compare only x } } void main() { auto s1 = S(1,2); auto s2 = S(1,3); auto s3 = S(2,1); assert(s1 < s3); // OK assert(s2 < s3); // OK assert(s3 > s1); // OK assert(s3 > s2); // OK assert(s1 <= s2 && s2 >= s1); // OK assert(s1 == s2); // FAIL -- WAT?? } ---- The reason for this is that the <, <=, >=, > operators are defined in terms of opCmp (which, btw, is defined to return 0 when the objects being compared are equal), but == is defined in terms of opEquals. When opEquals is not defined, it defaults to the built-in compiler definition, which is a membership equality test, even if opCmp *is* defined, and returns 0 when the objects are equal. Why isn't "a==b" rewritten as "a.opCmp(b)==0"?? I'm pretty sure TDPL says this is the case (unfortunately I'm at work so I can't check my copy of TDPL). https://issues.dlang.org/show_bug.cgi?id=13179 :-( T -- English has the lovely word "defenestrate", meaning "to execute by throwing someone out a window", or more recently "to remove Windows from a computer and replace it with something useful". :-) -- John Cowan |
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On 7/23/14, 1:45 PM, H. S. Teoh via Digitalmars-d wrote:
> This morning, I discovered this major WAT in D:
>
> ----
> struct S {
> int x;
> int y;
> int opCmp(S s) {
> return x - s.x; // compare only x
> }
> }
>
> void main() {
> auto s1 = S(1,2);
> auto s2 = S(1,3);
> auto s3 = S(2,1);
>
> assert(s1 < s3); // OK
> assert(s2 < s3); // OK
> assert(s3 > s1); // OK
> assert(s3 > s2); // OK
> assert(s1 <= s2 && s2 >= s1); // OK
> assert(s1 == s2); // FAIL -- WAT??
> }
> ----
>
> The reason for this is that the <, <=, >=, > operators are defined in
> terms of opCmp (which, btw, is defined to return 0 when the objects
> being compared are equal), but == is defined in terms of opEquals. When
> opEquals is not defined, it defaults to the built-in compiler
> definition, which is a membership equality test, even if opCmp *is*
> defined, and returns 0 when the objects are equal.
>
> Why isn't "a==b" rewritten as "a.opCmp(b)==0"?? I'm pretty sure TDPL
> says this is the case (unfortunately I'm at work so I can't check my
> copy of TDPL).
>
> https://issues.dlang.org/show_bug.cgi?id=13179
>
> :-(
>
>
> T
Imagine you have a list of integers and strings denoting integers: [1, "2", 100, "38"]. Now you want to sort them according to their numeric value. Of course, 1 and "1" would have the same order. However, 1 and "1" are different, so "==" would give false, while 1.opCmp("1") would give 0.
Equality and comparison are different. opCmp is used for sorting objects, which has nothing to do with equality. Inferring equality from opCmp is wrong in my opinion.
|
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ary Borenszweig | On Wednesday, 23 July 2014 at 17:15:12 UTC, Ary Borenszweig wrote:
> Imagine you have a list of integers and strings denoting integers: [1, "2", 100, "38"]. Now you want to sort them according to their numeric value. Of course, 1 and "1" would have the same order. However, 1 and "1" are different, so "==" would give false, while 1.opCmp("1") would give 0.
>
> Equality and comparison are different. opCmp is used for sorting objects, which has nothing to do with equality. Inferring equality from opCmp is wrong in my opinion.
Well this is why you can actually override those :) I think automatic opCmd -> opEqual generation covers vast majority of use cases and as such will have a vary good effort / decreased annoyance ratio.
|
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On 7/23/14, 11:09 AM, Dicebot wrote:
> On Wednesday, 23 July 2014 at 17:15:12 UTC, Ary Borenszweig wrote:
>> Imagine you have a list of integers and strings denoting integers: [1,
>> "2", 100, "38"]. Now you want to sort them according to their numeric
>> value. Of course, 1 and "1" would have the same order. However, 1 and
>> "1" are different, so "==" would give false, while 1.opCmp("1") would
>> give 0.
>>
>> Equality and comparison are different. opCmp is used for sorting
>> objects, which has nothing to do with equality. Inferring equality
>> from opCmp is wrong in my opinion.
>
> Well this is why you can actually override those :) I think automatic
> opCmd -> opEqual generation covers vast majority of use cases and as
> such will have a vary good effort / decreased annoyance ratio.
I agree. In fact I think if you've implemented opCmp to sort 1 and "1" as equal that in most cases you'd expect "1" and 1 to compare as logically equal. Automatic opCmp -> opEquals seems like a very sane default to me.
|
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On 7/23/14, 9:45 AM, H. S. Teoh via Digitalmars-d wrote:
> Why isn't "a==b" rewritten as "a.opCmp(b)==0"?? I'm pretty sure TDPL
> says this is the case (unfortunately I'm at work so I can't check my
> copy of TDPL).
>
> https://issues.dlang.org/show_bug.cgi?id=13179
>
> :-(
It's a good decision. There are types that are comparable for equality but not compared for ordering. -- Andrei
|
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On 7/23/14, 11:09 AM, Dicebot wrote:
> On Wednesday, 23 July 2014 at 17:15:12 UTC, Ary Borenszweig wrote:
>> Imagine you have a list of integers and strings denoting integers: [1,
>> "2", 100, "38"]. Now you want to sort them according to their numeric
>> value. Of course, 1 and "1" would have the same order. However, 1 and
>> "1" are different, so "==" would give false, while 1.opCmp("1") would
>> give 0.
>>
>> Equality and comparison are different. opCmp is used for sorting
>> objects, which has nothing to do with equality. Inferring equality
>> from opCmp is wrong in my opinion.
>
> Well this is why you can actually override those :) I think automatic
> opCmd -> opEqual generation covers vast majority of use cases and as
> such will have a vary good effort / decreased annoyance ratio.
I'd say let's leave things as they are. opEquals may need to do less work than opCmp, and it often sees intensive use. -- Andrei
|
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to David Gileadi | On 07/23/2014 11:26 AM, David Gileadi wrote: > On 7/23/14, 11:09 AM, Dicebot wrote: >> On Wednesday, 23 July 2014 at 17:15:12 UTC, Ary Borenszweig wrote: >>> Imagine you have a list of integers and strings denoting integers: [1, >>> "2", 100, "38"]. Now you want to sort them according to their numeric >>> value. Of course, 1 and "1" would have the same order. However, 1 and >>> "1" are different, so "==" would give false, while 1.opCmp("1") would >>> give 0. >>> >>> Equality and comparison are different. opCmp is used for sorting >>> objects, which has nothing to do with equality. Inferring equality >>> from opCmp is wrong in my opinion. >> >> Well this is why you can actually override those :) I think automatic >> opCmd -> opEqual generation covers vast majority of use cases and as >> such will have a vary good effort / decreased annoyance ratio. > > I agree. In fact I think if you've implemented opCmp to sort 1 and "1" > as equal that in most cases you'd expect "1" and 1 to compare as > logically equal. Automatic opCmp -> opEquals seems like a very sane > default to me. To add, C++ is getting "= default" versions: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3950.html Ali |
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Wednesday, 23 July 2014 at 18:49:49 UTC, Andrei Alexandrescu wrote:
> I'd say let's leave things as they are. opEquals may need to do less work than opCmp, and it often sees intensive use. -- Andrei
You will be the one answering user complaints ;)
|
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Wed, Jul 23, 2014 at 11:49:58AM -0700, Andrei Alexandrescu via Digitalmars-d wrote: > On 7/23/14, 11:09 AM, Dicebot wrote: > >On Wednesday, 23 July 2014 at 17:15:12 UTC, Ary Borenszweig wrote: > >>Imagine you have a list of integers and strings denoting integers: [1, "2", 100, "38"]. Now you want to sort them according to their numeric value. Of course, 1 and "1" would have the same order. However, 1 and "1" are different, so "==" would give false, while 1.opCmp("1") would give 0. > >> > >>Equality and comparison are different. opCmp is used for sorting objects, which has nothing to do with equality. Inferring equality from opCmp is wrong in my opinion. > > > >Well this is why you can actually override those :) I think automatic opCmd -> opEqual generation covers vast majority of use cases and as such will have a vary good effort / decreased annoyance ratio. > > I'd say let's leave things as they are. opEquals may need to do less work than opCmp, and it often sees intensive use. -- Andrei If autogenerating opEquals to be opCmp()==0 is a no-go, then I'd much rather say it should be a compile error if the user defines opCmp but not opEquals. Currently, we have the bad situation where == behaves inconsistently w.r.t. <, <=, >=, > because we allow opCmp to be defined but opEquals not, and the default compiler implementation of opEquals may or may not match the meaning of opCmp. In short, what I'd like to see, in order of preference, is: (1) If opCmp is defined but opEquals not, then opEquals should be defined to be opCmp()==0. (2) If (1) is a no-go, then the next best situation is that if opCmp is defined but opEquals isn't, then the compiler should issue an error, rather than implicitly generating a default opEquals that probably does not match the programmer's expectations. (3) If (2) is also a no-go, the 3rd best situation is that if opCmp is defined but opEquals isn't, then the compiler should issue an error if the user ever writes "a==b". That is, we allow the user to not define opEquals as long as it's not actually used, but it's an error if it is used. (4) Do nothing, and allow the current hidden breakage to perpetuate and make people hate D when they suddenly discover it when they forget to implement opEquals. I really hope we don't have to resort to (4). T -- All men are mortal. Socrates is mortal. Therefore all men are Socrates. |
July 23, 2014 Re: WAT: opCmp and opEquals woes | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | As of about 2 minutes ago, D-Scanner can help with this. --- struct A { int opCmp() const; } --- $ ./dscanner --styleCheck ~/tmp/test.d test.d(1:8)[warn]: 'A' has method 'opCmp', but not 'opEquals'. |
Copyright © 1999-2021 by the D Language Foundation