CLOS(3) メソッドがクラスに属しているように見えるようにする
お遊びとして、メソッドがクラスに属していないCLOSをmacroでラップして、メソッドがクラスに属しているように見えるようにしてみました。実用性 0%。
クラスは、define-class class-name slots methods という構文で定義するようにしました。method定義時は、インスタンスを表す引数(this AutoMobile)を書かずに、マクロで追加するようにしました。method定義内では、thisで自インスタンスを参照するようになります。これにより、メソッド定義では、一般的なOOPをサポートする言語と同じようにメソッドを定義できるようになりました。
(defmacro define-class (name super-classes slots methods) `(block nil (defclass ,name ,super-classes ,slots) ,@(loop for m in methods collect (destructuring-bind (method-name args &body body) m `(defmethod ,method-name ,(cons `(this ,name) args) ,@body)) ))) (define-class AutoMobile () ; slots ((name :accessor car-name-of :initarg :name)) ; methods ((disp-name () (format t "~a~%" (car-name-of this))) (refuel (oil) (format t "~a ~dL給油しました。~%" (car-name-of this) oil))))
lispでは、関数呼び出しは、(function-name args…)という形式なので、通常は、(obj method args…)という呼び出し方はできません。しかし、read macroを定義することで、似た感じにすることはできました。
メソッド呼び出しは、read macroを使って、新しい呼び出し形式を定義しました。
&(<インスタンス変数> <メソッド名> [<引数>+])
(set-macro-character #\& #'(lambda (stream char) (let ((sexp (read stream t nil t))) (destructuring-bind (instance method-name &optional args) sexp (if args `(,method-name ,instance ,args) `(,method-name ,instance))))))
rubyで以下の書き方を、
car1 = AutoMobile.new("vitz") car1.dispName car1.refuel 50
common lispでも下のように書けるようになりました。
(setq car1 (make-instance 'AutoMobile :name "vitz")) &(car1 disp-name) &(car1 refuel 50)
実行結果
vitz vitz 50L給油しました。
まとめ
read macroの強力さには改めて驚いた。
実際、common lispで、他の言語のOOPの書き方をシュミレーションすることは、比較的簡単だというのがわかった。勿論細かいところまでやろうとすればそれなりに大変でしょうけど。でも、やればやる程制限が追加されていくと感じた。CLOSの威力を発揮できなくなっていくのかもしれない。CLOSが、一見風変りに見える現在の形になっているというのは、そうなってしまったのではなく、過去のLisp使い達がこの形にしようとした結果なんだろうな。(具体的に理由はわからないですが、これから。。。)