2010/02/02

Common Lisp でのクラスメソッド

#|
Common Lisp でのクラスメソッド。
Common Lisp は (defclass foo ... のようにしても foo には何も束縛されない。
そうするとクラスメソッドを実装するにあたって困ってしまう。
|#


(defclass foo () ())
;; #<STANDARD-CLASS FOO>

(defclass bar (foo) ())
;; #<STANDARD-CLASS BAR>


#|
eql スペシャライザを使ってクラスメソッドを定義する。
|#

(defmethod f1 ((x (eql (find-class 'foo))))
(class-name x))
;; #<STANDARD-METHOD F1 ((EQL #<STANDARD-CLASS FOO>)) {1005489BA1}>

(f1 (find-class 'foo))
;; FOO

(f1 (find-class 'bar))
;; bar クラスを渡してもエラー

(f1 (make-instance 'foo))
;; インスタンスを渡してもエラー

(f1 (class-of (make-instance 'foo)))
;; FOO

#|
eql スペシャライザを使うのは find-class がめんどうだ。
そういえば make-instance はシンボルを引数にとる。
|#


(defmethod f2 ((x (eql 'foo)))
(class-name x))
;; #<STANDARD-METHOD F1 ((EQL #<STANDARD-CLASS FOO>)) {1005489BA1}>

(f2 'foo)
;; エラー

#|
シンボルだから class-name は適用できない。
あくまでもシンボルとして扱わなければならない。

それじゃ defclass の戻り値はクラス自身だから、それを defparameter で束縛してしまおう。
これで find-class は必要なくなる。
|#


(defparameter baz (defclass baz () ()))
;; BAZ

baz
;; #<STANDARD-CLASS BAZ>

(defmethod f3 ((x (eql baz)))
(class-name x))
;; #<STANDARD-METHOD F3 ((EQL #<STANDARD-CLASS BAZ>)) {1003E08771}>

(f3 baz)
;; BAZ

#|
これでもまだ eql がわずらわしい。

こうなったらメタクラスを使ってみよう。
|#


(defclass my-class (standard-class) ())
;; #<STANDARD-CLASS MY-CLASS>

(defmethod sb-mop:validate-superclass ((class my-class) (super standard-class))
t)
;; #<STANDARD-METHOD SB-MOP:VALIDATE-SUPERCLASS (MY-CLASS STANDARD-CLASS) {10039FC841}>

(defparameter hoge (defclass hoge ()
()
(:metaclass my-class)))
;; HOGE

hoge
;; #<MY-CLASS HOGE>

(defparameter huga (defclass huga ()
()
(:metaclass my-class)))
;; HUGA

huga
;; #<MY-CLASS HUGA>

(defmethod que ((x my-class))
(class-name x))
;; #<STANDARD-METHOD QUE (MY-CLASS) {1004B746D1}>

(que hoge)
;; HOGE

(que huga)
;; HUGA

#|
あるいは
|#


(defmethod que ((x symbol))
(que (find-class x)))
;; #<STANDARD-METHOD QUE (SYMBOL) {1004AA3471}>

(que 'hoge)
;; HOGE

(que 'huga)
;; HUGA

#|
どれもいまいち。すっきりしない。
Common Lisp でのクラスメソッドはどう定義したらいいのだろう?
|#

0 件のコメント: