SICPを読む(87) 問題 2.75 - 問題 2.76 メッセージパッシング

もうひとつのデータ主導実装。メッセージパッシングについて学ぶ。

メッセージパッシング

前回は、環境がメソッドを知っていた。「賢明な手続き」という。主にC++/Javaみたいなコンパイル型言語で使う方法かな。

今回のは、オブジェクトがメソッドを知ってる。「賢明なデータオブジェクト」という。つまり、ミニ環境を持った手続き(クロージャ)を返す。

Schemeなら、メッセージパッシングの方が断然スマートな解決法だと思う。


Wikipediaを見ると、OSでもメッセージパッシングが使われてる事がわかる。
メッセージ (コンピュータ) - Wikipedia
おぉ、システムコールはメッセージパッシングだ。イベントドリブンもメッセージパッシングだ。通信には非常に有用な手法なようだ。

問題 2.75

make-from-mag-angをメッセージパッシング流に直す問題。

(define (make-from-mag-ang x y)
  (lambda (op)
    (cond ((eq? op 'real-part) (* x (cos y)))
          ((eq? op 'imag-part) (* x (sin y)))
          ((eq? op 'magnitude) x)
          ((eq? op 'angle) y)
          (else
            (error "Unknown op" op)))))

(define z (make-from-mag-ang 1.0 (/ pi 6))) ; #<procedure>

(z 'real-part) ; 0.8660254037844387
(z 'imag-part) ; 0.49999999999999994

zオブジェクトに対して、メッセージを送ってるんだ。ふむふむ。ある意味通信してるように見える。オブジェクトに対するメッセージという発想は無かった。メソッドというより、メッセージだ。

なんか凄く新しいオブジェクト指向感があるな。メッセージパッシングかふむふむ。

で、apply-genericを通すと、

(define (apply-generic op arg) (arg op))

(define (real-part z) (apply-generic 'real-part z))

(real-part z) ; 0.8660254037844387

Scheme的にはこっちの方が自然かな。

問題 2.76

明白な振り分け、データ主導、メッセージパッシングについて学んだ。

新しい型、演算がどんどん追加されるときどれが適切なのかという問題。

新しい型の追加について考えてみる

明白な振り分け
  • 全ての演算に手を加える必要がある。
データ主導
  • 型を増やすだけ
メッセージパッシング
  • 型を増やすだけ

型を増やしても、中央システムに手を加える必要が無い。というのがデータ主導、メッセージパッシングの大きな特徴。

新しい演算の追加について考える

明白な振り分け
  • 演算を追加すてばいい。
データ主導
  • インターフェイスに手を加える必要がある。
  • 依存している型全てを更新しなければならない。
メッセージパッシング

横断的事柄に弱いというのがデータ主導の悲しさ。継承を使うことである程度は解消出来るが、色々大変。

まとめると

  • 型が少ない場合は、明白な振り分けが有用。
  • データ主導は、インターフェイスがあるので、確実な運用をすることが出来る。非常に面倒。
  • メッセージパッシングは宇宙のように色々出来るけど、自由過ぎる。
  • 型がどんどん増える場合は、データ主導、メッセージパッシング。
  • 演算がどんどん増える増える場合は、明白な振り分け。インターフェイスを持たないメッセージパッシング。
  • 型がどんどん増えて、演算がどんどん増えたら、インターフェイスを持ったデータ主導。メッセージパッシングでの手法は別にありそうな予感(多重継承とか)。

どれが適切かという問はよくわからない。「わかってたらSICPを読まない」というのが僕の答です。