delegation って何?

DelegateClass の謎 - ¬¬日常日記

さて、このように 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 のもやもやっとしたところが一気によく分かるようになりました。おお、なんてお得な感じなのでしょう。