Is there an opposite of 'eq' in Lisp?
Categories:
Understanding the Opposite of 'eq' in Lisp for Equality Checks

Explore various Lisp equality predicates and their 'opposite' forms, understanding when to use eq
, eql
, equal
, and equalp
for different data types and comparison needs.
In Lisp, the concept of 'equality' is more nuanced than in many other programming languages. The eq
predicate checks for object identity, meaning it returns true only if its arguments are literally the same object in memory. This often leads to the question: what is the opposite of eq
? While there isn't a single, direct 'neq' or 'not-eq' function that serves as a universal opposite, Lisp provides several equality predicates, each with its own definition of 'sameness'. Understanding these predicates and how to negate them is crucial for writing correct and robust Lisp code.
The Nuances of Lisp Equality Predicates
Lisp offers a spectrum of equality tests, each designed for specific scenarios. The 'opposite' of eq
isn't a single function, but rather the logical negation of eq
itself, or the use of other predicates that compare values rather than object identity. Let's delve into the primary equality predicates and their implications.
flowchart TD A[Start] --> B{Are objects identical (same memory location)?} B -- Yes --> C["Use `eq`"] B -- No --> D{Are objects of the same type and value?} D -- Yes --> E["Use `eql`"] D -- No --> F{Are objects structurally equal (same contents)?} F -- Yes --> G["Use `equal`"] F -- No --> H{Are objects structurally equal, ignoring case/type differences?} H -- Yes --> I["Use `equalp`"] H -- No --> J[Objects are not equal by any standard] C --> K[End] E --> K[End] G --> K[End] I --> K[End] J --> K[End]
Decision flow for choosing the correct Lisp equality predicate.
Understanding eq
, eql
, equal
, and equalp
Each Lisp equality predicate has a distinct purpose. Knowing when to use each one, and how to negate them, is fundamental to Lisp programming.
eq
when equal
is needed is a common source of bugs.eq
(Object Identity)
eq
is the most restrictive equality predicate. It returns T
only if its arguments are the exact same object in memory. For numbers and characters, eq
's behavior is implementation-dependent, but generally, it works reliably for symbols.
To get the 'opposite' of eq
, you simply negate its result using not
or null
(which is equivalent to not
for boolean values in Lisp).
;; Example of EQ
(setq a '(1 2 3))
(setq b '(1 2 3))
(setq c a)
(eq a b) ; => NIL (different objects, even if contents are the same)
(eq a c) ; => T (same object)
(eq 'foo 'foo) ; => T (symbols are often interned, so they are the same object)
;; Opposite of EQ
(not (eq a b)) ; => T
(null (eq a c)) ; => NIL
Demonstrating eq
and its negation.
eql
(Identity or Numeric/Character Equivalence)
eql
is a bit more relaxed than eq
. It returns T
if its arguments are eq
, or if they are numbers of the same type and value, or characters that are the same. For other types (like lists, strings, arrays), eql
behaves like eq
.
Its opposite is also achieved by negation.
;; Example of EQL
(eql 'a 'a) ; => T
(eql 1 1) ; => T
(eql 1.0 1.0) ; => T
(eql 1 1.0) ; => NIL (different types)
(eql #\a #\a) ; => T
(setq x "hello")
(setq y "hello")
(eql x y) ; => NIL (strings are not numbers/characters, so EQL acts like EQ)
;; Opposite of EQL
(not (eql 1 1.0)) ; => T
Demonstrating eql
and its negation.
equal
(Structural Equivalence)
equal
is the most commonly used predicate for comparing data structures. It returns T
if its arguments are eql
, or if they are lists, strings, bit-vectors, pathnames, or structures that have the same structure and equal
elements. It performs a recursive comparison.
Its opposite is (not (equal x y))
.
;; Example of EQUAL
(equal 'a 'a) ; => T
(equal 1 1) ; => T
(equal 1 1.0) ; => NIL (still different types)
(equal "hello" "hello") ; => T (strings are compared by value)
(equal '(1 2 3) '(1 2 3)) ; => T (lists are compared structurally)
(setq list1 '(a (b c) d))
(setq list2 '(a (b c) d))
(eq list1 list2) ; => NIL
(equal list1 list2) ; => T
;; Opposite of EQUAL
(not (equal '(1 2) '(1 2 3))) ; => T
Demonstrating equal
and its negation.
equalp
(Case-Insensitive Structural Equivalence)
equalp
is similar to equal
but is more permissive. It returns T
if its arguments are equal
, or if they are numbers that are numerically equal (even if different types, e.g., 1
and 1.0
), or characters that are the same ignoring case, or strings that are the same ignoring case. It's useful for comparisons where case or numeric type precision isn't critical.
Its opposite is (not (equalp x y))
.
;; Example of EQUALP
(equalp 'a 'a) ; => T
(equalp 1 1) ; => T
(equalp 1 1.0) ; => T (numerically equal)
(equalp #\a #\A) ; => T (case-insensitive character)
(equalp "hello" "Hello") ; => T (case-insensitive string)
(equalp '(1 "foo") '(1 "FOO")) ; => T (recursive, case-insensitive)
;; Opposite of EQUALP
(not (equalp "apple" "orange")) ; => T
Demonstrating equalp
and its negation.
Constructing the 'Opposite' of Equality
Since there's no single 'opposite' function like neq
in Lisp, you construct it by negating the appropriate equality predicate. The not
special operator (or its synonym null
) is used for this purpose. It returns T
if its argument is NIL
(false), and NIL
if its argument is non-NIL
(true).
When you need to check if two things are not equal, you simply wrap the chosen equality predicate in not
.
;; Not EQ
(defun not-eq (x y)
(not (eq x y)))
(not-eq 'a 'b) ; => T
(not-eq 'a 'a) ; => NIL
;; Not EQL
(defun not-eql (x y)
(not (eql x y)))
(not-eql 1 1.0) ; => T
(not-eql 1 1) ; => NIL
;; Not EQUAL
(defun not-equal (x y)
(not (equal x y)))
(not-equal '(1 2) '(1 2 3)) ; => T
(not-equal '(1 2) '(1 2)) ; => NIL
;; Not EQUALP
(defun not-equalp (x y)
(not (equalp x y)))
(not-equalp "foo" "bar") ; => T
(not-equalp "foo" "FOO") ; => NIL
Defining custom 'not-equal' functions using not
.
not-eq
, not-eql
, etc., it's often more idiomatic in Lisp to directly use (not (predicate x y))
within your code, especially in if
statements or cond
clauses.