Search code examples
common-lispsymbols

Symbol equality


In Paul Graham's book ANSI Common Lisp, while discussing symbol vs string manipulation, he says

Symbols can be compared in one step with eql ..."

(p138). Here are two cases of symbol comparison:

(setq x 3)
3
(setq a 'x)
X
(setq b 'x)
X
(eq a b)
T

So, since a and b point to the same symbol object, named "X", they are eq. However, this contrasts with

(setq a (copy-symbol 'x))
#:X
(setq b (copy-symbol 'x))
#:X
(symbol-name a)
"X"
(symbol-name b)
"X"
(eq a b)
NIL
(eql a b)
NIL
(equal a b)
NIL
(equalp a b)
NIL

Now, a and b point to different symbol objects, even though they have the same symbol-name and same print name. My questions are:

  1. why does Graham say eql rather than eq, and
  2. in the second case, why are a and b not at least equalp?

Solution

  • eql over eq?

    eql is "more predictable" in the sense that it is less implementation-dependent. E.g., non-immediate numbers (i.e., non-fixnums) can be non-eq in some implementations and eq in others:

    (= 1.5d0 1.5d0)
    => T
    (eql 1.5d0 1.5d0)
    => T
    (eq 1.5d0 1.5d0)
    => T in some implementations, NIL in others
    

    why aren't the identically named symbols not equalp?

    That's what the spec says :-)

    Note that it does violated the rule of thumb:

    A rough rule of thumb is that two objects are equal if and only if their printed representations are the same.

    The main non-histerical reason for this is that symbol, while being "atomic", still carries many baggage (e.g., variable and function bindings). IOW, a symbol is much more than its name.

    Also, one generally expects that equal forms evaluate to equal values, which cannot be the case if different symbols (with possibly different bindings!) are evaluated as equal.

    Now, the only differences between equal and equalp are

    1. case-insensitive character (and string) comparison
    2. comparing numbers using =
    3. recursively descending into structure, array, and hash-table.

    None of these changes affects the "similar evaluation rule" above, so there is no reason for equalp to compare symbols based on their names.

    If you want to compare symbols by names, you can use string=:

    (eq '#:a '#:a)
    ==> NIL
    (equalp '#:a '#:a)
    ==> NIL
    (string= '#:a '#:a)
    ==> T