Common Lisp symbol equality

Viewed 68

In most of the examples on the Internet, symbol equality is straight-forward:

(eq 'sym 'sym)
t

In my program, I want to compare symbols in a custom package:

(defpackage #:my-package
  (:use    #:common-lisp)
  (:export #:my-function))

(in-package #:my-package)

(defun my-function (value)
  (cond
    ((eq value 'sym)
     (format t "SYM: YES~%"))
    (t
     (format t "SYM: NO~%"))))

(in-package #:common-lisp)

(my-package:my-function 'sym)

But when this code is executed, it displays:

SYM: NO

It seems like the 2 symbols are different:

(eq 'sym 'my-package::sym)
nil

The reason seems easy to understand, a symbol interned in a given package is not equal to a symbol with the same name interned in another package. Fair enough! But what is the idiom to compare 2 symbols, regardless the package?

Should we convert it to a string in a first place and compare strings?

(defpackage #:my-package
  (:use    #:common-lisp)
  (:export #:my-function))

(in-package #:my-package)

(defun my-function (value)
  (cond
    ((string= (symbol-name value) "SYM")
     (format t "SYM: YES~%"))
    (t
     (format t "SYM: NO~%"))))

(in-package #:common-lisp)

(my-package:my-function 'sym)

The result is better, but there is an obvious issue regarding the character case.

How to check that 2 symbols are the same regardless their package?

2 Answers

The usual idiom is string=, which compares the names of symbols without regard to identity.

For example:

(eq 'x:a 'y:a) => nil
(string= 'x:a 'y:a) => t

I think you are confused about what 'being the same symbol' means.

Two symbols are the same symbol iff they are eq, which means that if they are not eq they are not the same symbol. In particular if two symbols have home packages which are different (again: not eq) they are not the same symbol.

If you want to know if two symbols have the same name then simply compare their symbol-names, as strings. But two symbols which merely have the same name are not necessarily the same symbol (for instance (eq '#:foo '#:foo) is false).

There are cases where it is useful to know whether two symbols have the same name (for instance the loop macro must do this so that (loop for ...) and (loop :for ...) mean the same thing) but these cases are fairly rare.

If what you want to do is know if two symbols have the same names you quite likely should be using strings instead.

If what you actually want to know is whether two symbols accessed from different packages are really the same symbol (so (eq 'x:foo 'y:foo), say) then eq is the appropriate test, and understanding the package system also helps.

Related