Is there an opposite of 'eq' in Lisp?

Learn is there an opposite of 'eq' in lisp? with practical examples, diagrams, and best practices. Covers if-statement, lisp, equality development techniques with visual explanations.

Understanding the Opposite of 'eq' in Lisp for Equality Checks

Hero image for Is there an opposite of 'eq' in Lisp?

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 (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.