たとえば、次のようなpackageとclassを定義したとする。
(in-package :cl-user)
(defpackage abc
(:use :cl)
(:export :<abc>))
(in-package :abc)
(defclass <abc> ()
((abc :initarg :abc
:reader get-abc
:documentation "abc")))
(in-package :cl-user)
(defpackage def
(:use :cl
:abc))
(in-package :def)
(defclass <def> (<abc>)
((def :initarg :def
:reader get-def
:documentation "def")))
で、defの中でinstanceを作成。
DEF> (setq wrk (make-instance '<def> :abc "111" :def "222"))
まぁ、WARNINGは多少でるけれど、中身は設定されている。
DEF> (get-def wrk)
"222"
DEF>
get-abcが効かないのは関数をexportしていないのでDEFから見えていないからだろう。
DEF> (get-abc wrk)
; in: GET-ABC WRK
; (DEF::GET-ABC DEF::WRK)
;
; caught STYLE-WARNING:
; undefined function: GET-ABC
;
; caught WARNING:
; undefined variable: WRK
;
; compilation unit finished
; Undefined function:
; GET-ABC
; Undefined variable:
; WRK
; caught 1 WARNING condition
; caught 1 STYLE-WARNING condition
; Evaluation aborted on #<UNDEFINED-FUNCTION GET-ABC {1003F36EE3}>.
DEF>
ところがslot-valueでabcの中身を見にいってもエラーになる。
DEF> (slot-value wrk 'def)
"222"
DEF> (slot-value wrk 'abc)
; Evaluation aborted on #<SIMPLE-ERROR "~@<When attempting to ~A, the slot ~S is missing from the ~
object ~S.~@:>" {10040EC8D3}>.
DEF>
これがしばらく理解できなかった。
というのもelispのEIEIOではうまくいくからだ。
HyperSpecのdefpackageの説明を見ていてexportしているのはsymbol-nameだと気づいた。functionじゃないんだ……。つまりClassの中で定義しているslot-nameもexportしなければ、いけない。
それならこれでも動くはず。
DEF> (slot-value wrk 'abc::abc)
"111"
DEF>
実際にexportしてやっても動いた。
(defpackage abc
(:use :cl)
(:export :<abc>
:abc))
DEF> (slot-value wrk 'abc)
"111"
DEF>
EIEIOで動いていていたのはpackageがないからか……。
でもslot-nameなんてばりばり競合してしまうでaccessorをexportするのが、吉なんだろうな……。accessorならmethodなので同じ名前が存在しても競合する可能性はきわめて低いから。