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