Eq

Equality in Common Lisp

1 exercise

About Equality

Common Lisp, like other languages has a set of rules on how to decide if two objects are the 'same'. These rules define four levels, each with a function that performs that level of checking. The levels are ordered from strictest to loosest.

eq

The first level is object identity. This equality is checked with the function eq. The two objects being checked for equality must be the very same object:

(eq 'apples 'apples)  ; => T
(eq 'apples 'oranges) ; => NIL

(eq '(a b c) '(a b c) ; => NIL (these two lists have the same contents but are not the same list)
(let ((list1 '(a b c)) (list2 list1)) 
  (eq list1 list2))   ; => T (these two lists are the same list)

eql

The second level adds equality of numbers and characters. This equality is checked with the function eql. The way the checking is done depends upon the types of the arguments:

  • Any two objects which are eq are eql
  • Numbers are eql if they are of the same type and value
  • Characters are eql if they they represent the same character.
(eql 1 1)     ; => T
(eql 1 1/1)   ; => NIL (one number is an integer, the other a rational)
(eql #\c #\c) ; => T
(eql #\c #\C) ; => NIL (case is different)

One may wonder why numbers and characters are not compared for object identity with eq. The Common Lisp standard allows implementations to copy numbers and characters if they choose to do so. Thus 0 and 0 may not be eq as they may be different instances of the number 0.

equal

The third level checks for structural similarity. This equality is checked with equal. The way the checking is done depends upon the types of the arguments:

  • symbols are compared as if with eq
  • characters, and numbers are compared as if with eql
  • conses are equal if their elements are equal. This is done recursively.
  • strings and bit vectors are equal if their elements are eql
  • arrays of other types are compared as if with eq
  • pathnames are equal if they are functionality equivalent. (There is room for implementation dependent behavior here with regards to case sensitivity of the strings which make up the components of the pathnames.)
  • objects of any other type are compared as if with eq
(equal '(a (b c)) '(a (b c)))         ; => T (conses are equal if their contents are equal)
(equal "hello" "hello")               ; => T
(equal "hello" "HELLO")               ; => NIL
(equal #(1 2 3) #(1 2 3))             ; => NIL (arrays are equal only if eq)
(equal #P"foo/bar.md" #P"foo/bar.md") ; => T (pathnames are equal if "functionally equivalent"

equalp

The fourth and most loose level of equality is checked with equalp. The how the checking is done depends upon the types:

  • if the two objects are equalp then they are equalp
  • numbers are equalp if they have the same value even if they are not of the same type
  • characters and strings are compared case-insensitively
  • conses are equalp if their elements are equalp. This is done recursively.
  • arrays are equalp if they have the same number of dimensions, those dimensions are the same, and each element is equalp.
  • structures are equalp if they have the same class and slots and each of those slots are equalp between the two structures.
  • hash tables are equalp if they both have the same :test function, they have the same keys (as compared with that :test function) and that those keys have the same values as compared with equalp.
(equalp 1 1.0)                       ; => T
(equalp #\c #\C)                     ; => T
(equalp "hello" "HELLO")             ; => T
(equalp #(1 2 3) #(1.0 2.0 3.0))     ; => T (arrays contain elements which are `equalp`)
(equal #S(TEST :SLOT1 'a :SLOT2 'b) 
       #S(TEST :SLOT1 'a :SLOT2 'b)) ; => T (structures of the same class with slots that have values which are `equalp`)

Type-specific functions

The above are the 'generic' equality functions. They work, as defined, for any type. This can be useful when one writes generic code that does not know the types of objects it will be comparing until run-time. However it is generally considered "better style" to use type specific equality functions when one knows the types being compared. For example string= rather than equal. These functions will be presented and discussed in the relevant concepts.

Edit via GitHub The link opens in a new window or tab

Learn Equality