delegation って何?
さて、このように Ruby の DelegateClass は良く分からない事でいっぱいです。しかし そもそも「delegation って何?」ということを理解していないと、MLで聞くことも出来ませんので delegation について調べてみることにしました。色々と調べていると、次のような記事をみつけることが出来ました。
http://roots.iai.uni-bonn.de/research/darwin/delegation
これはまさに「delegationって何なの?何じゃないの?」ということなので、私の目的にはぴったりです(ただしこれは lava っていう言語の解説ページの一部だから、lava における delegation とは、という可能性も高いのでこの内容がどれだけ一般化できるのかは分かりません)。ただし delegation という言葉は現実的には多義的に用いられてしまっているそうですので、これに説明されているのは色々な delegation の一つである、という事に注意しておく必要があるようです。概要をまとめておきます。
- Henry Lieberman さんが最初に delegation の用語を導入した
- 最初に delegation が実装されたのは Self などのプロトタイプベースの言語
- delegation という用語は不幸にも様々な使われ方をされてしまっている
- delegation と consultation の概念を区別しよう
- delegation はメッセージに対する振舞いを他のオブジェクトに委ね、self は自分
- consultation とメッセージ自体を他のオブジェクトに丸投げ、self は丸投げ先
- Invocation や Object extension とは一緒くたにしないでね
delegation と consultation の区別がとても興味深いですね。Ruby で書くと次のような違いということになるでしょうか。
class Parent def call; puts "self: #{self.inspect}"; end end # UnboundMethod の制限のため # Child は Parent を継承しておきますが他意はありません class Child < Parent def initialize @parent = Parent.new end end # delegation class ChildA < Child def call # @parent のメソッド call を取り出して self に束縛 # つまり振舞だけを @parent に譲る @parent.method(:call).unbind.bind(self).call end end # consultation class ChildB < Child def call # @parent にメッセージ call を譲る @parent.call end end ChildA.new.call # self: #<ChildA:0xb7c712b4 @parent=#<Parent:0xb7c71250>> ChildB.new.call # self: #<Parent:0xb7c7119c>
即ち、この定義に従えば DelegateClass で行なわれているのは delegation ではなくて consultation ということになります。
require "delegate" class Parent def call; puts "self: #{self.inspect}"; end end class Child < DelegateClass(Parent) def initialize super(Parent.new) end end Child.new.call # self: #<Parent:0xb7cc1cb4>
つまり標準の delegate.rb は実は全然 delegation してなくって、実際のところ consultation を行なうライブラリであることになります。
ただし、この定義における delegation とは、ほとんど Ruby の Mix-in と変わりません。
module Parent def call; puts "self: #{self.inspect}"; end end class Child include Parent end Child.new.call # self: #<Child:0xb7c509b0>
もしくは継承もまた delegation と変わらないように見えます。
class Parent def call; puts "self: #{self.inspect}"; end end class Child < Parent; end Child.new.call # self: #<Child:0xb7cf6a04>
そうするとここで言う delegation は Mix-in や継承で実現出来てしまっているので、DelegateClass で delegation を行なう必要は全然ないことになり、従って consultation こそ行なわれるべきであるのでしょう。
以上の話をまとめると、delegation と consultation は self が何であるのかによって区別されますが、この場合の delegation は Ruby は Mix-in もしくは継承によって実現できているために、敢えてライブラリとする必要はありません。よって delegate.rb では consultation の実現が目的とされているのでしょうし、DelegateClass がどうあるべきなのかを考える際にもこのことに注意しておくべきだろうと思います。
さて、ここまで来るとなぜ delegation のことを知りたいだけなのにプロトタイプベース言語の Self に行き着いたのかが、なんとなく分かってきました。delegation って早い話がプロトタイプベース言語における継承の代替物(と言っていいのかな?)として使われているわけですね。Self を試すのは大変なので(だってまだ文法もなにも分かってないんだもん!)Javascript で大体のイメージをつかんでみます。
var parent = { is: "parent", call: function () { print(this.is) } } function Child() { this.is = "child"; } Child.prototype = parent; new Child().call(); // child
delegation がプロトタイプベース言語の正体!と考えると、Javascript のもやもやっとしたところが一気によく分かるようになりました。おお、なんてお得な感じなのでしょう。