2018年7月9日月曜日

みたび、package

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