Common Lispシンボルについての覚書

シンボルはパッケージや「名前」に従属する存在ではなく、独立した実体を備えたオブジェクトである。

例えば以下のコードでは同じ名前を持つ2つの異なるシンボルを作っている。

(let ((sym1 (make-symbol "name"))
      (sym2 (make-symbol "name")))
  (values (string= (symbol-name sym1) (symbol-name sym2))
	  (eq sym1 sym2)))
;; => T NIL

これは他のオブジェクト、例えば文字列を make-array で作る場合と、質的には何ら変わりない。

(let ((str1 (make-array 4 :element-type 'character :initial-element #\a))
      (str2 (make-array 4 :element-type 'character :initial-element #\a)))
  (values (string= str1 str2)
	  (eq str1 str2)))
;; => T NIL

パッケージによるオブジェクト同一性の保証

シンボルは単なるオブジェクトなので、「『同じ名前』に対しては常に同じオブジェクトが割り当てられる」という、まさにシンボルがシンボルたる性質は、実はパッケージ・システムによって実現されている。

(let ((sym1 'foo)
      (sym2 'foo))
  (values (string= (symbol-name sym1) (symbol-name sym2))
	  (eq sym1 sym2)))
;; => T T

マクロにおけるオブジェクト同一性の保証

gensym を使ったマクロを書く場合などは、パッケージ・システムに頼らず自分でシンボル・オブジェクトの同一性を保証していることになる。

(defmacro sym-test ()
  (let ((sym (gensym)))
    `(eq ',sym ',sym)))
;; => T

上のコードにおいて sym はシンボル・オブジェクトに展開される(シンボルの「名前」ではない)ので、インターンされないシンボルを使っても、オブジェクトの同一性が保たれる。

つまり展開後のコードは、下のコードと等価では無い。

(eq '#:G762 '#:G762)
;; => NIL

展開後のコードに含まれているのは、あくまでもシンボル・オブジェクトである。

(mapcar #'(lambda (c) (class-of (cadr c))) (rest (macroexpand-1 '(sym-test))))
;; => (#<BUILT-IN-CLASS SYMBOL> #<BUILT-IN-CLASS SYMBOL>)

Leave a Reply