たとえば、次のような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なので同じ名前が存在しても競合する可能性はきわめて低いから。