WAT: opCmp and opEquals woes

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Sun Jul 27 17:21:54 PDT 2014


On Sun, Jul 27, 2014 at 07:04:08PM +0000, Fool via Digitalmars-d wrote:
> On Sunday, 27 July 2014 at 00:43:40 UTC, H. S. Teoh via Digitalmars-d wrote:
> >https://github.com/D-Programming-Language/dlang.org/pull/620
> 
> Thank you for this.
> 
> There is still a problem, I think.
> 
> 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.


> The user is not forced to define opEquals such that it models an
> equivalence relation but it hardly makes sense.

Well, in theory, if you *really* wanted to mess with the language (or
would-be readers of your code), you *could* implement an arbitrary
binary boolean predicate in opEquals, like defining a.opEquals(b) to be
ackermannFunction(a-17,b) == sqrt(19*zetaFunction(b/PI)).  The compiler
won't stop you -- and, due to computability theory, it can't, in
general, decide if your opEquals satisfies the axioms of an equivalence
relation -- but it begs the question, why? If you set out to shoot
yourself in the foot, it shouldn't be surprising if you blast your toes
off. :-P


> Similarly, the user is free to define opCmp without restriction. In
> practice, however, it does not seem to make any sense if <= does not
> even model a preorder (reflexive and transitive) or one of >, <=, <
> does not match.

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.


> It turns out that intuition of many people around here is not at
> random. Let A be a set and <= a preorder on A. For all a, b in A
> define ~ such that a ~ b = (a <= b or b <= a). Then ~ is an
> equivalence relation. (Let me know if you need a proof.)
> 
> Clearly, it is possible to define different equivalence relations on a
> set.  The same is true for orderings.
> 
> Now opEquals and opCmp are used to define a default equivalence
> relation and ordering on a type, respectively.
> 
> 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? ;-)

One-point compactification of the reals?

There are legitimate use cases for this, though admittedly, it's not
very common. That's why originally I proposed that opEquals would
*default* to opCmp()==0, but the user could override that if need be.


> Those examples mentioned before are skewed.
> 
> If a candidate for opCmp does not match the default equivalence
> relation == (defined implicitly or explicitly specified using
> opEquals) it should not be defined at all.

Well, certainly, they have to be consistent, otherwise you will get
strange results from your operators. :) But "consistent" may not
necessarily be opEquals = (opCmp()==0).


T

-- 
Life would be easier if I had the source code. -- YHL


More information about the Digitalmars-d mailing list