On this page:
equal<%>

6.8 Object Equality and Hashing🔗ℹ

By default, objects that are instances of different classes or that are instances of a non-transparent class are equal? only if they are eq?. Like transparent structures, two objects that are instances of the same transparent class (i.e., every superclass of the class has #f as its inspector) are equal? when their field values are equal?.

To customize the way that a class instance is compared to other instances by equal?, implement the equal<%> interface.

interface

equal<%> : interface?

The equal<%> interface includes three methods, which are analogous to the functions provided for a structure type with prop:equal+hash:
  • equal-to? Takes two arguments. The first argument is an object that is an instance of the same class (or a subclass that does not re-declare its implementation of equal<%>) and that is being compared to the target object. The second argument is an equal?-like procedure of two arguments that should be used for recursive equality testing. The result should be a true value if the object and the first argument of the method are equal, #f otherwise.

  • equal-hash-code-of Takes one argument, which is a procedure of one argument that should be used for recursive hash-code computation. The result should be an exact integer representing the target object’s hash code.

  • equal-secondary-hash-code-of Takes one argument, which is a procedure of one argument that should be used for recursive hash-code computation. The result should be an exact integer representing the target object’s secondary hash code.

The equal<%> interface is unusual in that declaring the implementation of the interface is different from inheriting the interface. Two objects can be equal only if they are instances of classes whose most specific ancestor to explicitly implement equal<%> is the same ancestor.
See prop:equal+hash for more information on equality comparisons and hash codes. The equal<%> interface is implemented with interface* and prop:equal+hash.

Example:
#lang racket
 
;; Case insensitive words:
(define ci-word%
  (class* object% (equal<%>)
 
    ;; Initialization
    (init-field word)
    (super-new)
 
    ;; We define equality to ignore case:
    (define/public (equal-to? other recur)
      (string-ci=? word (get-field word other)))
 
    ;; The hash codes need to be insensitive to casing as well.
    ;; We'll just downcase the word and get its hash code.
    (define/public (equal-hash-code-of hash-code)
      (hash-code (string-downcase word)))
 
    (define/public (equal-secondary-hash-code-of hash-code)
      (hash-code (string-downcase word)))))
 
;; We can create a hash with a single word:
(define h (make-hash))
(hash-set! h (new ci-word% [word "inconceivable!"]) 'value)
 
;; Lookup into the hash should be case-insensitive, so that
;; both of these should return 'value.
(hash-ref h (new ci-word% [word "inconceivable!"]))
(hash-ref h (new ci-word% [word "INCONCEIVABLE!"]))
 
;; Comparison fails if we use a non-ci-word%:
(hash-ref h "inconceivable!" 'i-dont-think-it-means-what-you-think-it-means)