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.

Reply via email to