Download - Meta-objective Lisp @名古屋 Reject 会議
Meta-objective Lisp
@dico_leque2011/02/26 NagoyaRejectKaigi
概要
n Common Lisp Object System 系の Metaobject
Protocol について説明
n 説明に使うのは Gauche Scheme interpreter のオブジェクトシステム
Lisp はオブジェクト指向言語n CLOS -- Common Lisp Object System
n 「しーろす」または「くろす」と読む
n ISLISP Object System -- ILOS というのもある
n Ruby にも色々影響を与えている
n ANSI Common Lisp 制定当時にあった Lisp 用オブジェクトシステムの上位互換を目指した柔軟性
速習 CLOS (1)
n クラスにはスロット(インスタンス変数)がある
n (define-class <foo> () ((slot1) (slot2)))
n 多重継承できる
n (define-class <baz> (<foo> <bar>) ())
速習 CLOS (2)n メソッドはクラスではなく総称関数(generic-
function)に属する
n 引数に応じて最も特定度の高いメソッドが呼ばれる(define-generic foo)(define-method foo ((x <sequence>) (y <sequence>)) 0)(define-method foo ((x <sequence>) (y <string>)) 1)(define-method foo ((x <string>) (y <sequence>)) 2)(define-method foo ((x <string>) (y <string>)) 3)
(foo '() '()) ; => 0(foo '() "a") ; => 1(foo "a" '#()) ; => 2(foo "a" "b") ; => 3
速習 CLOS (3)
n CLOS 自体も CLOS で書かれている
n メソッドの適用順の決定、インスタンス生成も総称関数として定義されている
n ユーザーは必要に応じて、通常の動作をオーバーライドできる
MOP
n Metaobject Protocol
n introspection: オブジェクトの内部表現にアクセスするための API
n intercessory: オブジェクトシステムの動作を変更するための API
n 今回は intercessory の話をします
Metaobjects
n オブジェクトの振る舞いを記述するオブジェクト
n <class> <generic> <method>
n オブジェクトシステムのデフォルトの振る舞いは
(make <class>) (apply-generic-function <generic>) といったメソッドで記述されている
例 (1) シングルトンパターン
n クラスのインスタンス生成をカスタマイズ
n <singleton-meta> クラスのインスタンスであるクラスは make すると常に同一のインスタンスが返る
n (instance-of <クラス>) でもインスタンスを取り出せる
n singleton なクラスのサブクラスも singleton になる
例 (1) シングルトンパターン(define-class <singleton-meta> (<class>) (%the-singleton-instance) )
(define-method make ((class <singleton-meta>) . initargs) ...)
(define-method instance-of ((class <singleton-meta>) . initargs) (apply make class initargs))
(define-class <singleton-mixin> () () :metaclass <singleton-meta>)
from gauche/lib/gauche/mop/singleton.scm
例 (2) O/R マッパー
n DB のカラムに対応するスロットを持つクラス
n <orm> をメタクラスとするクラスは、 DB アクセスによりスロット名が決定される
例 (2) O/R マッパー(define-class <orm> (<class>) ())
(define-method compute-slots ((cl <orm>)) (let ((super-slots (next-method))) (lset-union (lambda (x y) (eq? (car x) (car y))) (map (lambda (name) `(,name :init-keyword ,(make-keyword name) :accessor ,(string->symbol (format "~A-of" name)))) (load-slot-names cl)) super-slots)))
(define-class <foo> () () :metaclass <orm>)
※ load-slot-names は適当に定義する
例 (3) 特異クラスn Ruby の特異クラス風のメソッドディスパッチ
n ある値と等しい値を表すクラス
n (eql 0) を評価すると 0 だけをインスタンスとするクラスが返る
n <generic> のサブクラスを作り、 (eql x) をもっとも特定度の高い特定化子として扱う
例 (3) 特異クラス
(define-generic fact :class <eql-specializable-generic>)
(define-method fact ((n (eql 0))) 1)
(define-method fact ((n <integer>)) (* n (fact (- n 1))))
例 (3) 特異クラス(define-class <eql-specializer> (<class>) ((object :getter eql-specializer-object :init-keyword :eql-specializer-object)))
(define (eql obj) (make <eql-specializer> :eql-specializer-object obj))
(define-class <eql-specializable-generic> (<generic>) ())
(define-method compute-applicable-methods ((gf <eql-specializable-generic>) args) ...)
(define-method sort-applicable-methods ((gf <eql-specializable-generic>) methods args) ...)
※実際のコードは60行程度 http://d.hatena.ne.jp/leque/20110105/p1
CLOS / MOP で他にできること
n スロット(インスタンス変数)アクセスのカスタマイズ
n スロットの読み書きが DB アクセスになるようなクラス
n 他のオブジェクトに委譲するスロット
n validator 付きスロット
n メソッド適用規則のカスタマイズ
n クラス以外でディスパッチするメソッド Pascal Costanza, Charlotte Herzeel, Jorge Vallejos, Theo D’Hondt: “Filtered dispatch”, Proceedings of the 2008 symposium on Dynamic languages
n メソッドコンビネーション
n アスペクト指向風の before, after, around
n 適用可能なメソッドの戻り値を組み合わせる
CLOS / MOP で他にできること
n チューニング
n メソッドキャッシュの自作
n lazy initialization 等々
n 汎用オブジェクトシステムの作者よりもアプリケーション作者の方が今扱っている問題をよくわかっているはず
CLOS / MOP で他にできること
DSL と MOP
n DSL の対象 Domain によっては、言語の提供するオブジェクトシステムが合わない場合もある
n 既存の仕組みで頑張ってもよいけど、根本から変えた方がよい場合もある
まとめ
n Lisper にとってはオブジェクトシステムもただの
DSL
n CLOS MOP はオブジェクトシステムを書くためのフレームワーク