[plt-scheme] Re: When does eqv? differ from eq? , from equal?

From: Jacob Matthews (jacobm at cs.uchicago.edu)
Date: Tue Oct 7 03:57:46 EDT 2008

On Mon, Oct 6, 2008 at 11:20 PM,  <kbohdan at mail.ru> wrote:

> Does it mean that eq? [behavior] is platform specific?
> Looks worse than worse which is better :)

Depends on what you mean by eq's behavior -- you have to distinguish
between the behavior it's guaranteed to exhibit and behavior it may
happen to exhibit. eq? is defined by R6RS to be #t or #f in some
cases, and is undefined in many other cases. For instance,

(eq? '() '()) is defined to be #t
(eq? (list 1) (list 1)) is defined to be #f
(let ([x (list 1)]) (eq? x x)) is defined to be #t
(eq? (lambda (x) 1) (lambda (x) 2)) is defined to be #f
(boolean? (eq? X Y)) is defined to be #t for any values X, Y

(eq? (expt 10 100) (expt 10 100)) is undefined
(eq? 2 2) is undefined
(eq? (lambda (x) x) (lambda (x) x)) is undefined
(eq? (lambda (x) x) (lambda (y) y)) is undefined
(let ([x (lambda () 1)]) (eq? x x)) is undefined
(eq? (lambda (x) 72) (lambda (x) (if (valid-claim?
fermats-last-theorem) 72 43))) is undefined
(eq? (lambda () #f) (lambda () (valid-claim? p-equals-np))) is
conjectured but not proven to be undefined

mzscheme (and every other Scheme implementation) returns _something_
for all those undefined cases. But a portable program should never
rely on that return value. If you write programs that only use eq? in
the cases where it is defined by R6RS (which is probably a good
practice, even if you're only intending your program to run in
mzscheme), then its behavior is totally implementation-independent.
Otherwise you're on your own.


PS I say these things to explain eq?, not defend it. I'm not too happy
myself with the fact that equal? means "happens to be the same at this
moment" but eq? doesn't really mean "necessarily equal," or much of
anything else in particular. And eqv?, the function that started this
whole topic, is useless as near as I can tell, since it is defined to
be eq? unless you know the values you're comparing are numbers or
characters (or a handful of "empty" values) in which case you're
better off using a type-specific comparison function.

But then again, every programming language I know of has at least a
few frustrating dark corners when it comes to equality. It's a much
harder problem than it appears. This is especially true in the
presence of mutation, where the notions "happens to be the same right
now" and "necessarily equal" are very often conflated. But even in
totally pure settings, "equality" is overloaded to mean lots of
different, similar-but-sometimes-slightly-different things. I was
reminded of this most recently when I wrote a bit of graph-traversal
code in Haskell, with the data representation something like  "data
Vertex = V [(Char, Vertex)]", and graph cycles constructed with tricks
like "let aStar = Vertex [('a', aStar)] in aStar". This is a perfectly
legitimate way to define a graph in Haskell, but how do you tell if
you've already traversed a given node?

Posted on the users mailing list.