#:g1: coolの紹介

Posted 2014-02-24 15:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の56日目です。

coolとはなにか

 coolは、Common Lispにオブジェクト指向を取り入れる際にCommonLoopsや、Flavors、Object LISPと共に検討されたオブジェクト指向システムの一つである、CommonObjectsの実装一つです。

パッケージ情報

パッケージ名cool
Quicklisp
参考サイトPackage: lang/lisp/oop/non_clos/cool/

インストール方法

 上記のCMUのサイトからダウンロードしてロードします。もしくは、SBCLで動くようにしてASDFに対応したものがありますので、興味のある方は試してみて下さい

Quicklispのlocal-project以下に展開すれば、

(ql:quickload :cool)

でもロード可能です。

試してみる

 Sheepleの時と同様に、BankAccountというのを書いてみます。


(define-type bank-account
  (:var dollars (:init 0) :initable :gettable :settable))

(define-method (bank-account :deposit) (n) (incf dollars n))

(define-method (bank-account :withdraw) (n) (setf dollars (max 0 (- dollars n))))

(defparameter *my-account* (make-instance 'bank-account :dollars 200)) ;=> *MY-ACCOUNT*

(=> *my-account* :dollars) ;=> 200

(=> *my-account* :deposit 50) ;=> 250

(=> *my-account* :withdraw 100) ;=> 150

(=> *my-account* :withdraw 200) ;=> 0

(=> *my-account* :dollars) ;=> 0

 スロットの初期化可能、参照可能、書き込み可能は、:initable、:gettable、:settableで指定するところが、Flavorsと似ています。
ここでCLOSに馴れた人からすると目に付くのは、メソッド本体の中で変数がローカル変数として参照できることでしょうか。
=> は 所謂sendです。CLOSのように多重メソッドではありません。

 そして、このbank-accountを継承した、stock-account。
stock-accountのdollarsがスロットの値とは独立に(同じ結果を)計算するのがどうも気持ち悪いのですが、オーバーロードできることを示しているようでもあるらしいので、そのまま書きます。


(define-type stock-account 
  (:inherit-from bank-account)
  (:var num-shares (:init 0))
  (:var price-per-share (:init 30))
  :all-initable
  :all-gettable)

(define-method (stock-account :set-dollars) (n) (setf num-shares (/ n price-per-share)) (call-method (bank-account :set-dollars) n))

(define-method (stock-account :dollars) () (* num-shares price-per-share))

(define-method (stock-account :deposit) (n) (=> self :set-dollars (+ n (call-method (bank-account :dollars)))))

(define-method (stock-account :withdraw) (n) (=> self :set-dollars (+ (- n) (call-method (bank-account :dollars)))))

(defparameter *my-stock* (make-instance 'stock-account :num-shares 10)) ;=> *MY-STOCK*

(=> *my-stock* :dollars) ;=> 300

(=> *my-stock* :set-dollars 600) ;=> 600

(=> *my-stock* :num-shares) ;=> 20

(=> *my-stock* :deposit 60) ;=> 660

(=> *my-stock* :num-shares) ;=> 22

(=> *my-stock* :withdraw 120) ;=> 540 (=> *my-stock* :num-shares) ;=> 18

 CommonObjectsに特徴的なのは、親のスロットをマージしない点です。
同名スロットがぶつかることがないのでその辺りは気にしなくても良いのですが、参照する場合は、継承関係に関係なくcall-methodで参照する必要があります。つまり継承関係は関係なく公開されたスロットにしか外部からはアクセスできない、ということになります。
とはいえ、インスタンスのスロットは同じオブジェクト内に同居しているので、なんとも不思議な感覚です。
:all-gettable等の:allはスロット全部に対しての指定。これもFlavorsに似ています。

 スロットとは対照的にメソッドはデフォルトで親のものを全部引き継ぎます。
引き継ぎたくない場合は、(:method)節で指定しますが、ホワイトリストなので一つのメソッドだけ引き継ぎたくないという場合面倒な感じです。
その他、CommonObjectsは多重継承だったりしますが、多重継承特有の問題については、まだ調べてません…。

coolの紹介/関連記事等

まとめ

 今回は、coolを紹介してみました。
coolはかなり初期の1986年位のPortable CommonLoopsの上に実装されていますが、そのうちCLOS MOPで書き直してみたいところです。

comments powered by Disqus