July 24, 2014
I wonder. If opCmp is supposed to imply partial ordering, then that means opCmp should imply the antisymmetric property of partial ordering. http://mathworld.wolfram.com/PartialOrder.html

a <= b and b <= a implies a = b.

That would mean for us that opEquals being generated with opCmp == 0 would make sense.

Without that transitive property, it would imply only that it was a preorder, but not a partial order. http://mathworld.wolfram.com/Preorder.html
July 24, 2014
On Thursday, 24 July 2014 at 01:39:01 UTC, H. S. Teoh via Digitalmars-d wrote:

> Keep in mind, though, that due to current AA changes in 2.066 beta,
> existing code WILL break unless we autogenerate opEquals to be
> opCmp()=0. In fact, issue 13179 was originally filed because 2.066 beta
> broke Tango. My current stance is that these AA changes are an
> improvement that we should keep, so then the question becomes, should we
> break code over it, or should we introduce opEquals = (opCmp()==0),
> which would allow existing code *not* to break?

Can we just adjust the AA implementation so that it uses lhs.opCmp(rhs) == 0 if opEquals isn't defined and produces a deprecation warning about that? That way, we avoid immediately breaking folks, but we still move towards requiring that they define opEquals.

- Jonathan M Davis
July 24, 2014
On Thursday, 24 July 2014 at 01:37:01 UTC, deadalnix wrote:
> On Thursday, 24 July 2014 at 00:28:06 UTC, Jonathan M Davis wrote:
>> On Wednesday, 23 July 2014 at 21:36:16 UTC, Andrei Alexandrescu wrote:
>>> On 7/23/14, 12:04 PM, H. S. Teoh via Digitalmars-d wrote:
>>>> 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.
>>>
>>> No. There is this notion of partial ordering that makes objects not smaller and not greater than others, yet not equal. --
>>
>> I would strongly argue that if lhs.opCmp(rhs) == 0 is not equivalent to lhs == rhs, then it that type is broken and should not be using opCmp to do its comparisons. std.algorithm.sort allows you to use any predicate you want, allowing for such orderings, but it does not work with generic code for a type to define opCmp or opEquals such that they're not consistent, because that's not consistent with how comparisons work for the built-in types.
>>
>> - Jonathan M Davis
>
> floating point ?

When it comes to equality and comparison, floating point values are mess that I would really hope no one would be looking to emulate with their own types. I grant you that they're a built in type, but they do not have clean semantics (particularly with regards to equality). IMHO, user-defined types should emulate integers with regards to how the comparison operators work. Allowing more nonsense like what FP does does not improve things.

- Jonathan M Davis
July 24, 2014
On 7/24/14, 11:30 AM, Jonathan M Davis wrote:
> On Thursday, 24 July 2014 at 01:39:01 UTC, H. S. Teoh via Digitalmars-d
> wrote:
>
>> Keep in mind, though, that due to current AA changes in 2.066 beta,
>> existing code WILL break unless we autogenerate opEquals to be
>> opCmp()=0. In fact, issue 13179 was originally filed because 2.066 beta
>> broke Tango. My current stance is that these AA changes are an
>> improvement that we should keep, so then the question becomes, should we
>> break code over it, or should we introduce opEquals = (opCmp()==0),
>> which would allow existing code *not* to break?
>
> Can we just adjust the AA implementation so that it uses lhs.opCmp(rhs)
> == 0 if opEquals isn't defined and produces a deprecation warning about
> that? That way, we avoid immediately breaking folks, but we still move
> towards requiring that they define opEquals.

I like Daniel's idea to auto-define opEquals as a field-for-field comparison. -- Andrei
July 24, 2014
On Thursday, 24 July 2014 at 19:35:12 UTC, Andrei Alexandrescu wrote:
> I like Daniel's idea to auto-define opEquals as a field-for-field comparison. -- Andrei

That's basically what the compiler-generated opEquals does (though I think that it'll just to a memcmp if it knows that it can get away with that). So, if that's what you want, you're arguing for just have the compiler still define opEquals for you even if opCmp is defined. And I'm fine with that, but if the concern is code breakage for AAs, and opCmp is not defined in a way that's consistent with opEquals, then that would break them. Now, I think that such types are buggy, so I'm not sure that that's all that big a deal, but if I understand correctly, basically anything that we do with 2.066 which doesn't involve continuing to use lhs.opCmp(rhs) == 0 for the AAs will break them, and we need to change it to use opEquals, so I'm not sure that we _can_ avoid code breakage unless the type defines opEquals, and it defines it in a manner which is consistent with opCmp (which it _should_ do but might not). And if that's the case, then it just comes down to what type of code breakage we want to incur. And IMHO, having the default opEquals continue to be generated when opCmp is defined is a very workable solution, since no types should have defined opCmp in a way that was inconsistent with opEquals.

- Jonathan M Davis
July 24, 2014
On Thu, Jul 24, 2014 at 08:36:02PM +0000, Jonathan M Davis via Digitalmars-d wrote:
> On Thursday, 24 July 2014 at 19:35:12 UTC, Andrei Alexandrescu wrote:
> >I like Daniel's idea to auto-define opEquals as a field-for-field comparison. -- Andrei

Isn't that what the compiler already does?


> That's basically what the compiler-generated opEquals does (though I think that it'll just to a memcmp if it knows that it can get away with that). So, if that's what you want, you're arguing for just have the compiler still define opEquals for you even if opCmp is defined. And I'm fine with that, but if the concern is code breakage for AAs, and opCmp is not defined in a way that's consistent with opEquals, then that would break them. Now, I think that such types are buggy, so I'm not sure that that's all that big a deal, but if I understand correctly, basically anything that we do with 2.066 which doesn't involve continuing to use lhs.opCmp(rhs) == 0 for the AAs will break them, and we need to change it to use opEquals, so I'm not sure that we _can_ avoid code breakage unless the type defines opEquals, and it defines it in a manner which is consistent with opCmp (which it _should_ do but might not). And if that's the case, then it just comes down to what type of code breakage we want to incur. And IMHO, having the default opEquals continue to be generated when opCmp is defined is a very workable solution, since no types should have defined opCmp in a way that was inconsistent with opEquals.
[...]

Keep in mind that the last sentence means that wrong code (i.e. inconsistent opCmp/opEquals) will silently compile and misbehave at runtime.


T

-- 
Answer: Because it breaks the logical sequence of discussion. Question: Why is top posting bad?
July 25, 2014
On Thursday, 24 July 2014 at 21:54:01 UTC, H. S. Teoh via Digitalmars-d wrote:
> On Thu, Jul 24, 2014 at 08:36:02PM +0000, Jonathan M Davis via Digitalmars-d wrote:
>> And IMHO, having the default opEquals continue to be generated when
>> opCmp is defined is a very workable solution, since no types should
>> have defined opCmp in
>> a way that was inconsistent with opEquals.
> [...]
>
> Keep in mind that the last sentence means that wrong code (i.e.
> inconsistent opCmp/opEquals) will silently compile and misbehave at
> runtime.

Well, that's exactly the behavior in 2.065 from what I can tell. If you don't define opEquals, the compiler defines it for you even if you defined opCmp. And trying out git head, it does exactly the same thing. You only get a compilation error when using AAs, which is pretty weird IMHO, and it seems very wrong to suddenly require that opEquals be defined when the default is still being generated. The only way that anyone is going to have problems is if their opCmp is not consistent with opEquals, which is just plain a bug IMHO, so if switching the AAs to opEquals instead of opCmp causes bugs, you're just swapping one set of bugs for another, which seems fine to me. Certainly, I think that it's stupid to require that opEquals be defined just because you're using the type as an AA key when it's not required otherwise.

- Jonathan M Davis
July 25, 2014
On 7/23/2014 9:45 AM, H. S. Teoh via Digitalmars-d wrote:
> https://issues.dlang.org/show_bug.cgi?id=13179

Consider also:

http://www.reddit.com/r/programming/comments/2bl51j/programming_in_d_a_great_online_book_for_learning/cj75gm9

The current scheme breaks existing code - code that was formerly correct and working.

AAs don't make sense if the notion of == on members is invalid. AAs formerly required opCmp, and yes, opCmp could be constructed to give different results for opCmp==0 than ==, but I would expect such an object to not be used in an AA, again because it doesn't make sense.

Using the default generated opEquals for AAs may break code, such as the an AA of the structs in the parent post, but it seems unlikely that that code was correct anyway in an AA (as it would give erratic results).

Kenji's rebuttal https://issues.dlang.org/show_bug.cgi?id=13179#c2 is probably the best counter-argument, and I'd go with it if it didn't result in code breakage.
July 25, 2014
On 25 July 2014 14:50, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 7/23/2014 9:45 AM, H. S. Teoh via Digitalmars-d wrote:
>
>> https://issues.dlang.org/show_bug.cgi?id=13179
>>
>
> Consider also:
>
> http://www.reddit.com/r/programming/comments/2bl51j/ programming_in_d_a_great_online_book_for_learning/cj75gm9
>
> The current scheme breaks existing code - code that was formerly correct and working.
>
> AAs don't make sense if the notion of == on members is invalid. AAs formerly required opCmp, and yes, opCmp could be constructed to give different results for opCmp==0 than ==, but I would expect such an object to not be used in an AA, again because it doesn't make sense.
>
> Using the default generated opEquals for AAs may break code, such as the an AA of the structs in the parent post, but it seems unlikely that that code was correct anyway in an AA (as it would give erratic results).
>
> Kenji's rebuttal https://issues.dlang.org/show_bug.cgi?id=13179#c2 is probably the best counter-argument, and I'd go with it if it didn't result in code breakage.
>

I don't really see how opCmp == 0 could be unreliable or unintended. It was
deliberately written by the author, so definitely not unintended, and I
can't imagine anybody would ever deliberately ignore the == 0 case when
implementing an opCmp, or produce logic that works for less or greater, but
fails for equal.
<= and >= are expressed by opCmp, which imply that testing for equality
definitely works as the user intended.

In lieu of an opEquals, how can a deliberately implemented opCmp, which we know works in the == case (otherwise <= or >= wouldn't work either) ever be a worse choice than an implicitly generated opEquals?

Personally, just skimming through this thread, I find it baffling that this is controversial.


July 25, 2014
On Friday, 25 July 2014 at 04:50:38 UTC, Walter Bright wrote:
> On 7/23/2014 9:45 AM, H. S. Teoh via Digitalmars-d wrote:
>> https://issues.dlang.org/show_bug.cgi?id=13179
>
> Consider also:
>
> http://www.reddit.com/r/programming/comments/2bl51j/programming_in_d_a_great_online_book_for_learning/cj75gm9
>
> The current scheme breaks existing code - code that was formerly correct and working.
>
> AAs don't make sense if the notion of == on members is invalid. AAs formerly required opCmp, and yes, opCmp could be constructed to give different results for opCmp==0 than ==, but I would expect such an object to not be used in an AA, again because it doesn't make sense.
>
> Using the default generated opEquals for AAs may break code, such as the an AA of the structs in the parent post, but it seems unlikely that that code was correct anyway in an AA (as it would give erratic results).

Exactly. The only reason that switching from using lhs.opCmp(rhs) == 0 to opEquals would break code is if a type does not define them such that they're equivalent, which would mean that opEquals and/or opCmp was defined in a buggy manner. So, the only way that the change would break code is if it was broken in the first place. All it risks is making it so that the bug exhibits itself in an additional case.

> Kenji's rebuttal https://issues.dlang.org/show_bug.cgi?id=13179#c2 is probably the best counter-argument, and I'd go with it if it didn't result in code breakage.

Yeah. It wouldn't be all that bad to do something similar to C++11 and make it so that we explicitly indicate when we want to use the default opEquals (or toHash), but doing so would break code, and outright just making it so that opEquals must be defined when AAs are used without allowing a way for the compiler-generated opEquals or toHash to be used seems very broken to me. With such a change, _no_ existing user-defined type would be able to use the built-in opEquals or toHash functions if they're used with AAs, which is particularly bad in the case of toHash, since that's much harder to get right.

- Jonathan M Davis