あどけない話

インターネットに関する技術的な話など

Lisp で多態性

On Lisp を理解するためのメモです。

オブジェクト指向Lisp

オブジェクト指向Lispの章には以下のような記述があります。

CLOSの到来はLispオブジェクト指向パラダイムを取り込み始めた兆に見えるかも知れない.実際には,Lispオブジェクト指向パラダイムを今も変わらず含んでいると言う方が正確だ.しかしLispの底を流れる原則には名前が無く,オブジェクト指向プログラミングには名前があるので,現在,Lispオブジェクト指向言語だと説く傾向がある.「Lispは拡張可能な言語で,その内部でオブジェクト指向プログラミングが容易に行える」と言う方が真実に近いだろう.

そして、こうも書かれています。

実際,拡張性はオブジェクト指向スタイルの最大の利点の一つだ.プログラムは単一のモノリシックなコードの塊になるのではなく,小さい部品毎に書かれ,各々に目的に従ってラベルが付く.よって後日,誰か別の人がプログラムを修正したいと思ったとき,変更の必要な部分は容易に見つかる.... この観点からすると,オブジェクト指向プログラムとは表のように構成されたプログラムだ.適切な項目を探すことで直ちに安全に変更できる.

さらに。

拡張性のためにオブジェクト指向スタイルが必要になる,ということはまずない.実際,拡張可能なプログラムはオブジェクト指向である必要は全くない.前の章で示したのは, Lispプログラムがモノリシックなコードの塊である必然性はないということに過ぎない. Lispは拡張性のあらゆる選択肢を提供する.例えば,正に文字通り表のように構成されたプログラムが書ける.配列に保持したクロージャの集合から成るプログラムだ.

さてさて、これは一体どういう意味でしょうか?

オブジェクトとしてのクロージャ

Elisp だとよく知っているのですが、動的スコープでクロージャがありません。そこで、勉強を兼ねて Scheme で書きます。

以下のように配列に保持したクロージャの集合から成るプログラムを書きます。

(define (new-obj1 x)
  (vector
   (lambda () x)
   (lambda (_x) (set! x _x))))

これは抽象データ型と言えますし、もう少し大きな口をたたくとオブジェクトだとも言えます。以下のように使います。

(define obj1 (new-obj1 0))
((vector-ref obj1 1) 20)
((vector-ref obj1 0))
;; => 20

これだと配列の添字である 0 とか 1 とかが出て来て、メッセージっぽくありません。

Graham さんの主張は、マクロを書けば、オブジェクト指向風の表記を実現できるということなんでしょうが、現時点ではどうすればいいのか分りません。X(

メッセージっぽく

マクロではなく、内部にディスパッチャを定義する方法を考えてみます。計算機プログラムの構造と解釈からコードを借用すると、こう書けます。

(define (new-obj2 x)
  (define (get-x) x)
  (define (set-x _x) (set! x _x))
  (define (dispatch m)
    (cond
     ((eq? m 'get-x) get-x)
     ((eq? m 'set-x) set-x)
     (else (error "Unknown message" m))))
  dispatch)

(define obj2 (new-obj2 0))
((obj2 'set-x) 20)
((obj2 'get-x))
;; => 20

感想

「サブ型を使った多態性は、実装のテクニックに過ぎない」という主張があります。この例を見る限り、それも正しい気もします。