chickadee » scheme » eqv?

(eqv? obj[1] obj[2])procedure

The eqv? procedure defines a useful equivalence relation on objects. Briefly, it returns #t if obj[1] and obj[2] should normally be regarded as the same object. This relation is left slightly open to interpretation, but the following partial specification of eqv? holds for all implementations of Scheme.

The eqv? procedure returns #t if:

  • obj[1] and obj[2] are both #t or both #f.
  • obj[1] and obj[2] are both symbols and
   (string=? (symbol->string obj1)
             (symbol->string obj2))
               ===>  #t

Note: This assumes that neither obj[1] nor obj[2] is an "uninterned symbol" as alluded to in the section on symbols. This report does not presume to specify the behavior of eqv? on implementation-dependent extensions.

  • obj[1] and obj[2] are both numbers, are numerically equal (see =, under numerical operations), and are either both exact or both inexact.
  • obj[1] and obj[2] are both characters and are the same character according to the char=? procedure (see "characters").
  • both obj[1] and obj[2] are the empty list.
  • obj[1] and obj[2] are pairs, vectors, or strings that denote the same locations in the store.
  • obj[1] and obj[2] are procedures whose location tags are equal (see "procedures").

The eqv? procedure returns #f if:

  • obj[1] and obj[2] are of different types.
  • one of obj[1] and obj[2] is #t but the other is #f.
  • obj[1] and obj[2] are symbols but
   (string=? (symbol->string obj[1])
             (symbol->string obj[2]))
               ===>  #f
  • one of obj[1] and obj[2] is an exact number but the other is an inexact number.
  • obj[1] and obj[2] are numbers for which the = procedure returns #f.
  • obj[1] and obj[2] are characters for which the char=? procedure returns #f.
  • one of obj[1] and obj[2] is the empty list but the other is not.
  • obj[1] and obj[2] are pairs, vectors, or strings that denote distinct locations.
  • obj[1] and obj[2] are procedures that would behave differently (return different value(s) or have different side effects) for some arguments.
(eqv? 'a 'a)                             ===>  #t
(eqv? 'a 'b)                             ===>  #f
(eqv? 2 2)                               ===>  #t
(eqv? '() '())                           ===>  #t
(eqv? 100000000 100000000)               ===>  #t
(eqv? (cons 1 2) (cons 1 2))             ===>  #f
(eqv? (lambda () 1)
      (lambda () 2))                     ===>  #f
(eqv? #f 'nil)                           ===>  #f
(let ((p (lambda (x) x)))
  (eqv? p p))                            ===>  #t

The following examples illustrate cases in which the above rules do not fully specify the behavior of eqv?. All that can be said about such cases is that the value returned by eqv? must be a boolean.

(eqv? "" "")                     ===>  unspecified
(eqv? '#() '#())                 ===>  unspecified
(eqv? (lambda (x) x)
      (lambda (x) x))            ===>  unspecified
(eqv? (lambda (x) x)
      (lambda (y) y))            ===>  unspecified

The next set of examples shows the use of eqv? with procedures that have local state. Gen-counter must return a distinct procedure every time, since each procedure has its own internal counter. Gen-loser, however, returns equivalent procedures each time, since the local state does not affect the value or side effects of the procedures.

(define gen-counter
  (lambda ()
    (let ((n 0))
      (lambda () (set! n (+ n 1)) n))))
(let ((g (gen-counter)))
  (eqv? g g))                   ===>  #t
(eqv? (gen-counter) (gen-counter))
                                ===>  #f
(define gen-loser
  (lambda ()
    (let ((n 0))
      (lambda () (set! n (+ n 1)) 27))))
(let ((g (gen-loser)))
  (eqv? g g))                   ===>  #t
(eqv? (gen-loser) (gen-loser))
                                ===>  unspecified

(letrec ((f (lambda () (if (eqv? f g) 'both 'f)))
         (g (lambda () (if (eqv? f g) 'both 'g))))
  (eqv? f g))
                                ===>  unspecified

(letrec ((f (lambda () (if (eqv? f g) 'f 'both)))
         (g (lambda () (if (eqv? f g) 'g 'both))))
  (eqv? f g))
                                ===>  #f

Since it is an error to modify constant objects (those returned by literal expressions), implementations are permitted, though not required, to share structure between constants where appropriate. Thus the value of eqv? on constants is sometimes implementation-dependent.

(eqv? '(a) '(a))                         ===>  unspecified
(eqv? "a" "a")                           ===>  unspecified
(eqv? '(b) (cdr '(a b)))                 ===>  unspecified
(let ((x '(a)))
  (eqv? x x))                            ===>  #t

Rationale: The above definition of eqv? allows implementations latitude in their treatment of procedures and literals: implementations are free either to detect or to fail to detect that two procedures or two literals are equivalent to each other, and can decide whether or not to merge representations of equivalent objects by using the same pointer or bit pattern to represent both.