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.