July 25, 2014
On 7/25/2014 1:02 AM, Jacob Carlborg wrote:
> 3. If opCmp is defined but no opEquals, lhs == rhs will be lowered to
> lhs.opCmp(rhs) == 0

This is the sticking point. opCmp and opEquals are separate on purpose, see Andrei's posts.

July 25, 2014
Putting it simply,

1. == uses opEquals. If you don't supply opEquals, the compiler will make one for you.

2. AAs use ==. See rule 1.


Easy to understand, easy to explain, easy to document.
July 25, 2014
On 7/25/2014 1:27 AM, Jacob Carlborg wrote:
> On 25/07/14 08:50, Walter Bright wrote:
>
>> Yes, that's why it's hard to see that it would break existing code,
>> unless that existing code had a bug in it that was worked around in some
>> peculiar way.
>
> If the type was only used as an AA key and never checked for equivalent then it
> worked correctly when opCmp was used for AA keys.
>
> Also, adding an opEqual to call opCmp == 0 will make it work for equivalent as
> well, even though it's never used. And it will fix the breaking change with AA
> keys.

The thing is, either this suffers from == behaving differently than AAs, or you've made opEquals superfluous by defining it to be opCmp==0. The latter is a mistake, as Andrei has pointed out, as opCmp may not have a concept of equality, and opEquals may not have a concept of ordering.

I.e. it's not just about AAs.

July 25, 2014
On 7/25/2014 1:28 AM, Jacob Carlborg wrote:
> If the type is only used as an AA key and never checked for equivalent it worked
> when opCmp as used for AA keys.

Then we'll just get another bug report from AAs behaving differently from ==.

July 25, 2014
On 25/07/14 10:55, Walter Bright wrote:

> Then we'll just get another bug report from AAs behaving differently
> from ==.

No, not as long as it's not used.

-- 
/Jacob Carlborg
July 25, 2014
On 25/07/14 10:48, Walter Bright wrote:
> Putting it simply,
>
> 1. == uses opEquals. If you don't supply opEquals, the compiler will
> make one for you.
>
> 2. AAs use ==. See rule 1.
>
>
> Easy to understand, easy to explain, easy to document.

It's very hard to use D when it constantly changes and breaks code. It's especially annoying reading your comments on reddit that we must stop break code. Then a few days later go an break code. I really hope no one gets false hopes from those comments.

-- 
/Jacob Carlborg
July 25, 2014
On 7/25/2014 2:04 AM, Jacob Carlborg wrote:
> On 25/07/14 10:55, Walter Bright wrote:
>
>> Then we'll just get another bug report from AAs behaving differently
>> from ==.
>
> No, not as long as it's not used.

Well, that's a forlorn hope :-)

July 25, 2014
On 7/25/2014 2:12 AM, Jacob Carlborg wrote:
> On 25/07/14 10:48, Walter Bright wrote:
>> Putting it simply,
>>
>> 1. == uses opEquals. If you don't supply opEquals, the compiler will
>> make one for you.
>>
>> 2. AAs use ==. See rule 1.
>>
>>
>> Easy to understand, easy to explain, easy to document.
>
> It's very hard to use D when it constantly changes and breaks code. It's
> especially annoying reading your comments on reddit that we must stop break
> code. Then a few days later go an break code. I really hope no one gets false
> hopes from those comments.

We went through the likely code breakage from this in this thread, and it's hard to see any non-broken code breaking. It will also fix regression https://issues.dlang.org/show_bug.cgi?id=13179 and stop that breakage.

July 25, 2014
On Friday, 25 July 2014 at 08:21:26 UTC, Jacob Carlborg wrote:
> By defining opEquals to be opCmp == 0 we're:
>
> 1. We're not breaking code where it wasn't broken previously
> 2. We're fixing broken code. That is when opEqual and opCmp == 0 gave different results

Code that worked perfectly fine before is now slower, because it's using opCmp for opEquals when it wasn't before. Even worse, if you define opEquals, you're then forced to define toHash, which is much harder to get right. So, in order to avoid a performance hit on opEquals from defining opCmp, you now have to define toHash, which significantly increases the chances of bugs. And regardless of the increased risk of bugs, it's extra code that you shouldn't need to write anyway, because the normal, default opEquals and toHash worked just fine.

I honestly have no sympathy for anyone who defined opCmp to be different from the default opEquals but didn't define opEquals. Getting that right is simple, and it's trivial to test for you're unit testing like you should be. I don't want to pay in my code just to make the compiler friendlier to someone who didn't even bother to do something so simple. And any code in that situation has always been broken anyway. I'm _definitely_ not interested in reducing the performance of existing code in order to fix bugs in the code of folks who couldn't get opEquals or opCmp right.

I'd much rather be able to take advantage of the fast, default opEquals and correct toHash than be forced to define them just because I defined opCmp and didn't want a performance hit on opEquals.

- Jonathan M Davis
July 25, 2014
On Friday, 25 July 2014 at 08:02:18 UTC, Jacob Carlborg wrote:
> 1. If neither opCmp or opEquals are defined, the compiler will automatically generate these and will be used for comparison and equivalent
>
> 2. If opEquals is defined, lhs == rhs will be lowered to lhs.opEquals(rhs)
>
> 3. If opCmp is defined but no opEquals, lhs == rhs will be lowered to lhs.opCmp(rhs) == 0
>
> 4. If opCmp and opEquals is defined, lhs == rhs will be lowered to lhs.opEquals(rhs)

The compiler _never_ defines opCmp for you. You have to do that yourself. So, what you're suggesting would force people to define opEquals just because they defined opCmp unless they wanted to take a performance hit. And once you define opEquals, you have to define toHash. So, what you're suggesting would force a lot more code to define toHash, which will likely cause far more bugs than simply requiring that the programmer define opEquals if that's required in order to make it consistent with opEquals.

- Jonathan M Davis