WAT: opCmp and opEquals woes
Don via Digitalmars-d
digitalmars-d at puremagic.com
Mon Jul 28 04:00:18 PDT 2014
On Monday, 28 July 2014 at 06:05:03 UTC, Fool wrote:
> On Monday, 28 July 2014 at 00:23:36 UTC, H. S. Teoh via
> Digitalmars-d wrote:
>> On Sun, Jul 27, 2014 at 07:04:08PM +0000, Fool via
>>> Defining opEquals only makes sense if a user wants to replace
>>> equality
>>> by some equivalence relation (different from equality).
>>
>> Not necessarily. The user type may be implemented in a way
>> where
>> member-wise binary comparison is not the correct
>> implementation of
>> equality. For example, it could be a tree structure
>> implemented by
>> integer indices into a backing array of nodes. There is no way
>> the
>> compiler could know, in general, how to correctly compare two
>> instances
>> of such a structure, since the bit-level representation of two
>> equal
>> objects may be completely different, yet they represent
>> equivalent
>> trees. You're still implementing equality, but it's equality
>> that's not
>> the same as binary equality.
>
> I think we agree except for a subtle difference in defining
> equality and equivalence. In my personal language there is a
> single equality but there are many equivalences.
>
>
>> The problem with imposing these kinds of restrictions, is that
>> they are
>> generally not enforceable (at least, not without significantly
>> crippling
>> legitimate use cases). At some point, we have to stop
>> babysitting the
>> programmer and trust that he's competent enough to not try to
>> subvert
>> the language to make it do stuff it wasn't intended to do. As
>> somebody
>> once said:
>>
>> Unix was not designed to stop people from doing stupid things,
>> because that would also stop them from doing clever things.
>> -- Doug Gwyn
>>
>> We're not talking about Unix here, but the same principle
>> applies.
>
> I agree.
>
>
>>> Please excuse my lack of creativity: in presence of opCmp I
>>> cannot see
>>> a single sensible use case for defining a.opEquals(b)
>>> different from
>>> a.opCmp(b) == 0.
>>
>> Floating-point numbers? ;-)
>
> Thank you for pushing me there! It's true.
>
> So D has to separate opEquals and opCmp since otherwise a user
> could not define floating-point 'equality' and 'comparison'
> himself in the same way as it is defined by the language.
>
> I'm convinced know. :-)
>
> Thanks!
Be careful, though. The argument that opCmp() and opEquals() are
orthogonal is not correct, though. Although they are different
concepts, they are closely related.
We must have: a == b implies a.opCmp(b) == 0.
The converse does not apply though.
Otherwise you're abusing operator overloading, like when you
define + to mean "reformat hard disk" or something.
Suppose we dealt correctly with floating point, including the <>=
operators, etc. Then we'd require another overloaded operator.
bool unordered(X other) // return true if !(this > other) &&
!(this < other)
Full situation is:
opCmp() == 0 implies ( a==b || a.unordered(b) )
This applies to the RGB example, too.
If you define opCmp() for a type, then either:
(1) opEquals() is the same as opCmp()==0, OR
(2) opEquals() is weird, and needs to be explicitly defined. What
you're really doing is distinguishing the unordered case from the
equal case.
IMHO, the ideal solution would be a really smart compiler that
can detect violations of (1). At least, it would be fairly simple
to add a runtime assert that this.opCmp(this) == 0 for all cases
where opEquals is synthesised.
More information about the Digitalmars-d
mailing list