Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2008-05-08

CADRでFLAVORS

| 16:10 | CADRでFLAVORS - わだばLisperになる を含むブックマーク はてなブックマーク - CADRでFLAVORS - わだばLisperになる

CADRエミュレータで何かしようと思いつつも前回からまた一ヶ月経ってしまいました。

最近CLOSのような、LISP上でのOOPになんとなく興味があるのですが、CADRには、CLOS以前のflavorsというOOPの枠組がありました。

よくRubyなどで、mixinとかいう用語が使われますが、この用語は、確かflavorsに由来していたと思います。

アイスクリームのトッピングに由来した用語なのですが、flavorsという名前自体、そのトッピングのことを指してるみたいです。

とりあえず、何から始めたら良いか良く分からないので、ポール・グレアム著のANSI CLのCLOSの章をなぞってみることにしました。

とりあえず、構造体とクラスを比較してみる、という内容なのですが、defstructの細かいところが、CLとLisp Machine Lispで違っていたりして、それもまた乙です。

とりあえず、CLの構造体だとこんな感じです。

;; CL
(defstruct rectangle
  height
  width)

(defstruct circle
  radius)

(defun area (x)
  (cond ((rectangle-p x)
         (* (rectangle-height x) (rectangle-width x)))
        ((circle-p x)
         (* pi (expt (circle-radius x) 2)))))

(let ((r (make-rectangle)))
  (setf (rectangle-height r) 2
        (rectangle-width r) 3)
  (area r))
;=> 6

自動で、rectangle-widthのようなアクセサを作ってくれて、rectangle-pのような述語も作成してくれます。

次にLisp Machine Lispでの構造体でのバージョンです。

;; Lisp Machine Lisp
(defstruct (rectangle :named)
  rectangle-height
  rectangle-width)

;; アクセッサはフルネームじゃないと駄目
;; namedにしないと、
;; (typep obj 'rectangle)とか効かないので、
;; namedで作らないとrectangle-pのようなものが作れないらしい。

(defstruct (circle :named)
  circle-radius)

;; piは定数ではないので定義
(defconst pi 3.141592653589793238)

;; 自動で、述語を作ってくれないので自作
(defmacro make-pred (name)
  `(defun ,(intern (string-append (string name) "-P")) (obj)
     (typep obj ',name)))

(make-pred rectangle)
(make-pred circle)

(defun area (x)
  (cond ((rectangle-p x)
         (* (rectangle-height x) (rectangle-width x)))
        ((circle-p x)
         (* pi (expt (circle-radius x) 2)))))

(let ((r (make-rectangle)))
  (setf (rectangle-height r) 2)
  (setf (rectangle-width r) 3)
  (area r))
;=> 6

そして、Lisp Machine LispのFlavorsで

;; Lisp Machine Lisp
(defflavor rectangle (height width) ()     
  :gettable-instance-variables
  :settable-instance-variables)

(defflavor circle (radius) ()
  :gettable-instance-variables
  :settable-instance-variables)

(defmethod (rectangle :area) ()
  (* (funcall-self ':height) (funcall-self ':width)))

(defmethod (circle :area) ()
  (* pi (expt (funcall-self ':radius) 2)))

(let ((r (make-instance 'rectangle)))
  (<<-- r
        (':set-height 2)
        (':set-width 3)
        (':area)))
;=> 6

ざっとした説明ですが、まず、キーワードに一々クオートが付いています。

これは、Lisp Machine Lispでは、CLのようにキーワードパッケージがあって、そのシンボルの評価結果が自分自身となるようなものではなく、ただの目印的なものなので、クオートが必要になります。

次に、defflavorですが、これは、CLOSのdefclassに相当します。

書式は、

(defflavor クラス(フレイバー)名 (スーパークラス)
  ...)

のような感じで、

:gettable-instance-variables

:settable-instance-variables

:inittable-instance

は値を取得/設定/初期化できるようにする指示で、デフォルト値も設定できます。

とりあえず、フレーバーを作ったら、

(setq r (make-instance 'rectangle))

(<- r ':set-width 3)

のようにして、値を設定できたりします。

rというオブジェクトに:set-widthと3というメッセージを送るのですが、

(funcall r ':set-width 3)

でも良かったりするみたいです。

ちなみに<-は後に、sendという名前になったようです。

set-〜というところは、
settable-instance-variablesをすると自動的に:set-〜で値を設定することができるようになります。

それで、defmethodですが、

(defmethod (フレーバー キー?) (引数)
  ...)

となり、CLのdefmethodとはちょっと違っています。

呼び出し方ですが、

(<- r ':area)

のようになるようです。

funcall-selfですが、defmethod内では自身を指すselfという変数が使えるので、これは、(<- self ...mesg)と同じことのようです。

CLOSのように(area r)とは書けないみたいでなんですが、初期のFlavorsの試行錯誤の後、「関数呼び出しの形とメソッド呼び出しの形を一緒にしたら良いんじゃないか?」というアイディアが現われて形が統合されたらしいです。

<<--は、(progn (<- ..) (<- ..))に展開されるマクロで上の例は、

(let ((r (make-instance 'rectangle)))
  (<- r ':set-height 2)
  (<- r ':set-width 3)
  (<- r ':area))

と同じです。

まとめ

という感じで、自分も全然分かっていないので、解説らしい解説にもなっていないのですが、色々いじって遊んでみようと思っています!