Ruby

【Ruby】Method クラスとは

たびたび失礼しております。 Method というクラスを調べました。何かよくないことを書いてあったらコメントください。よろしくお願いします。

さてこれは何なんでしょうか。ある方のコードを見ていた時のことです。そのソースは忘れちゃいました(苦笑)。メソッドの引数として Proc オブジェクトを渡せますが、その & に、何やらシンボルで表したメソッドを渡していたのです。クラスがその名を冠した定数を持つのと似たように、メソッドをシンボルで表せることは知っていましたが、これは何だろうと思って調べた時に、Method クラスというものが登場しました。 Method クラスは Proc クラスに少し似ていると思いました。

名前が名前なので検索エンジンも何のことを調べたがっているのかややこしいらしく、検索結果はクラスメソッドとかクラスについてとかそんなページしか見つかりません。仕方ありませんが公式ドキュメントが頼りです。難しくても頑張って考えてみるしかないです。

class Method

公式によれば以下の説明です。

Object#method によりオブジェクト化され たメソッドオブジェクトのクラスです。

メソッドの実体(名前でなく)とレシーバの組を封入します。 Proc オブジェクトと違ってコンテキストを保持しません。

これは、Object#method を使って作成されたオブジェクトのクラスが Method となる、という意味のようです。Object#class で聞いてみます。

# メソッドを定義
def foo(arg)
  "Called with arg #{arg}"
end

# メソッドの実体を格納
obj = method(:foo)
# 格納されたメソッドに引数を渡して実行
p obj.call("Hello")
# あなたは誰?
p obj.class
# 格納されていたメソッドが返した結果
"Called with arg Hello"

# そのメソッドをオブジェクト化
# そしてそのオブジェクトのクラスとは
Method

Object#method が生成するのは Method クラスのインスタンス、ということであってたみたいですね。格納されていたメソッドのクラスを問いているのではありません。あくまでそのメソッドをオブジェクト化したものが Method クラスのようです。

次に、以下は公式ドキュメントのサンプルコードで、既存のメソッドを Method オブジェクト化しているものです。

# クラスとメソッドを定義
class Foo
  def foo(arg)
    "foo called with arg #{arg}"
  end
end

# Foo クラスのインスタンスを作成
# Foo#foo を Method オブジェクトとして格納
m = Foo.new.method(:foo)

# Method オブジェクトとして格納された Foo#foo
p m
# Method#call が引数を Foo#foo に渡す
p m.call(1)
#<Method: Foo#foo>
"foo called with arg 1"

ところで、上記の引用で出てきた「封入」を大辞林で調べると以下のようになっています。

ふうにゅう―にふ0【封入】
(名)スル
① 封筒などに入れて封をすること。また,同封すること。「写真を―する」
② 封じ込めること。「電球にアルゴンガスを―する」

引用の「メソッドの実体(名前ではなく)とレシーバの組を封入」という記述の「レシーバ」が分かりづらくないでしょうか。はじめ、自分はこの言葉の意味を特異メソッドのような感覚で「メソッドとレシーバをセットでオブジェクト化する」ものだと思っていました。しかし、違うようです。

上のサンプルコードですが、これだと Method クラスの便利さが分からないということで以下のサンプルコードが掲載されています。

# クラスとメソッドを定義
class Foo
  def foo
    'foo'
  end

  def bar
    'bar'
  end

  def baz
    'baz'
  end
end

# Foo のインスタンスを作成
obj = Foo.new

# それをレシーバとする method のセットを
# ハッシュにしてキーと関連づける
methods = {
            1 => obj.method(:foo),
            2 => obj.method(:bar),
            3 => obj.method(:baz)
            }

# ハッシュの中でレシーバと method がセットになっており
# キーで各式を呼び出して評価し出力
p methods[1].call
p methods[2].call
p methods[3].call
"foo"
"bar"
"baz"

ハッシュで関連づけて使う時にレシーバを固定しておけますということらしいですね。「メソッドの実体(名前ではなく)とレシーバを組として封入」という言葉は、ハッシュのような構造で実現しているだけな気がしますが、そうではないんでしょうか。どういった意味で「組を封入する」と説明してるのか、よく分かりません。