Ruby の Method オブジェクトとは

今一つピンとこない Ruby の `Method` オブジェクトについて解説を試みた。

簡潔さのため，あえていくつかの Ruby の基礎的概念について解説を省いたけれど，読んで分からないところがあったら，どんな初歩的なことでも遠慮なくコメントでお尋ねください。


# Method オブジェクトってなんやねん？

よく「Ruby はすべてがオブジェクト」というけれど，これは誤解を招く。
「Ruby ではすべての**データが**オブジェクト」と表現すべきだ。

データでないものはオブジェクトでない。
たとえば変数も制御構造もスクリプトもメソッドもブロックも演算子も，データではないからオブジェクトではない。

さて，メソッドはオブジェクトではないのだが，オブジェクト化することはできる。メソッドをオブジェクト化したものは `Method` クラスのインスタンスであり，「Method オブジェクト」と呼ばれる。

これは，
〈ブロックそのものはオブジェクトではないが，オブジェクト化することはでき，オブジェクト化したものは「Proc オブジェクト」と呼ばれる〉
ことと似ている。


`Method` オブジェクトと `Proc` オブジェクトは，その役割や使い方もよく似ている。
どちらも何らかの処理を表しており，`call` や `[ ]` を使って呼び出す（その処理を実行させる）ことができる。

また，`Method` オブジェクトは [Method#to_proc](https://docs.ruby-lang.org/ja/2.6.0/method/Method/i/to_proc.html) を使って `Proc` オブジェクトに変換することもできる。

# 作り方

`Object` クラスには，`Method` オブジェクトを作るための [method](https://docs.ruby-lang.org/ja/2.6.0/method/Object/i/method.html) というメソッドがある。
オブジェクトに対し，メソッド名を引数として `method` を呼び出すと，当該メソッドをオブジェクト化した `Method` オブジェクトを作って返してくれる。

「は？？？　なんのこっちゃ？」

サンプルコードを見たら一発で分かる：

```rb
s = "Ruby"
m = s.method(:upcase)
```

これで，`m` には，〈文字列 `"Ruby"` をレシーバーとする `upcase` メソッドをオブジェクト化したもの〉が代入される。

`Method` オブジェクトは〈何らかのクラスやモジュールにおけるメソッド定義単体をオブジェクト化したもの〉ではなく，**特定のオブジェクトに結びついている**ということが本質的に重要だ。

`Method` オブジェクトのレシーバーは，[Method#receiver](https://docs.ruby-lang.org/ja/2.6.0/method/Method/i/receiver.html) で得られる：

```rb
m = "Ruby".method(:upcase) 
p m.receiver # => "Ruby"
```

メソッド名は [Method#name](https://docs.ruby-lang.org/ja/2.6.0/method/Method/i/name.html) で得られる：

```rb
m = "Ruby".method(:upcase) 
p m.name # => :upcase
```

「だから何なんだよ！　こんなの意味あんのかよ！」（イライラ）

# 使い方

`Method` オブジェクトは [Method#call](https://docs.ruby-lang.org/ja/2.6.0/method/Method/i/=3d=3d=3d.html) を使って呼び出すことができる。

```rb
m = "Ruby".method(:upcase)

p m.call # => "RUBY"
```

「は？　こんなことして何が嬉しいわけ？？？」

`Method` オブジェクトはレシーバーが固定されているため，上の例でいうと〈**いろいろな**文字列を大文字化する〉のには使えない。

このあたりで「もうええわ，ワシには関係あらへん」と読むのをやめてしまいたくなるね。かつての私もそうだった。

えー，気を取り直して，メソッドが引数を持っているケースを見てみよう。

`Integer` クラスには，文字列化するメソッド `to_s` があるが，引数を与えることで $n$ 進法の文字列にできる：

```rb
x = 2989

p x.to_s # => "2989" （デフォルトは 10 進法）
p x.to_s(2) # => "101110101101"
p x.to_s(16) # => "bad"
```

これを `Method` オブジェクトでやってみよう：

```rb
m = 2989.method(:to_s)

p m.call # => "2989"
p m.call(2) # => "101110101101"
p m.call(16) # => "bad"
```

「いや，あのさ，確かに引数を変えていろいろな結果が得られる `Method` オブジェクトはできたけどさ，やっぱ**何が嬉しいのかわかんねーわ**」

では，`call` の代わりに `[ ]` を使ってみよう：

```rb
m = 2989.method(:to_s)

p m[] # => "2989"
p m[2] # => "101110101101"
p m[16] # => "bad"
```

「いや確かに短くはなったけどさ，短くなっただけじゃん？」

コードが簡潔に書けることはそれだけで価値があったりもするよ。

「いや，そもそも，固定した一つの数をいろんな $n$ 進法で文字列化するっていう場面があんまり思いつかないんだけど？」

# 何に使うの？

`Method` オブジェクトが今一つピンとこないのは，用途がよく分からないせいだろう。
`Method` オブジェクトの解説を見ても，上記と大差ないサンプルコードが載っていたりして，釈然としない。
「例のための例」でしかないからだ。

`Proc` オブジェクトに比べて出番が少ない理由の一つは，既に見たように〈レシーバーを固定して同じメソッドを何度も呼び出す〉という場面が，〈さまざまなレシーバーに対して同じメソッドを何度も呼び出す〉場面よりも少ないことだろう。

実際，`Method` オブジェクトを使うコードとして，レシーバーに関知しない[^fm]**関数的メソッド**をオブジェクト化するものをよく見る。関数的メソッドはレシーバーが何でもいいんだから，〈レシーバーが変えられない〉ことは欠点にならない。

[^fm]: 「レシーバーに関知しない」とは，ここでは「動作がレシーバーに依存せず，レシーバーを変化させたりもしない」ことを指している。

現実にはどういう場面で使われるのか？

## ブロックを取るメソッドに & で与える

### 前振り（Proc の場合）

`Method` オブジェクトの話をいったん脇に置いておいて，姉妹とも言える `Proc` オブジェクトの，以下のような使い方を見てみよう。

```rb
languages = ["Ruby", "Python", "Rust"]
p languages.map(&:downcase) # => ["ruby", "python", "rust"]
```

この `map(&:downcase)` は要素を全部小文字にするものだが，見た目の簡潔さとはうらはらに，ちょっと複雑な機構が働いている。
`map` はブロックを取るが，ブロックを与える代わりに，`Proc` オブジェクトを `&` 付きで渡すと，その `Proc` オブジェクトが**ブロックとして**渡されたことになる。

しかし，`map(&:downcase)` の場合，`&` を付けているのは `Proc` オブジェクトではなくシンボルである。
この場合，[Symbol#to_proc](https://docs.ruby-lang.org/ja/2.6.0/method/Symbol/i/to_proc.html) が暗黙に呼び出されて `Proc` オブジェクトができ，それがブロックとしてメソッドに渡される。
この書き方は，裏の仕組みはややこしいけれど，ひとたび理解して慣れてしまえば思考の経済になる。単に短いというよりは簡潔なのだ。

### Proc みたくメソッドに渡す

前振りが長くなった。`Method` オブジェクトに話を戻そう。

まず，16 進法や 2 進法などのプリフィクス（それぞれ `0x`, `0b`）の付いた文字列の配列が与えられて，それを整数の配列に変換することを考える。
この目的には，[String#to_i](https://docs.ruby-lang.org/ja/2.6.0/method/String/i/to_i.html) でなく [Kernel.#Integer](https://docs.ruby-lang.org/ja/2.6.0/method/Kernel/m/Integer.html) が向いている。

```rb
p Integer("100") # => 100　（プリフィクス無しは 10 進法）
p Integer("0x100") # => 256　（16 進法なので）
p Integer("0b100") # => 4　（2 進法なので）
```

このコードの `Integer` はクラスではなくメソッドだ。

「おい，ちょっと待て。メソッド名は小文字始まりじゃねーのかよ！」

文法的にはそんなルールはない。
とはいえ，大文字始まりのメソッド名は特別な場合に限って使うべきだろう。典型的には

* レシーバーに関知しない関数的メソッドで，
* `Kernel` に定義されて（つまりどこででも使え），
* 引数を元に，**同名のクラスの**インスタンスを生成する

ようなメソッドだ。

さて，この `Integer` メソッドを使うとこんなふうに書ける：

```rb
integer_strings = ["100", "0x100", "0b100"]

p integer_strings.map{ |str| Integer(str) }
# => [100, 256, 4]
```

ふう，ようやく `Method` オブジェクトの出番が来たぞ。
上記のサンプルは `Method` オブジェクトを使って以下のように書ける：

```rb
integer_strings = ["100", "0x100", "0b100"]

p integer_strings.map(&method(:Integer))
# => [100, 256, 4]
```

さて，これはどのように解釈されるのだろうか。
まず

```rb
method(:Integer)
```

を見よう。この `method( )` は，`Object#method` の呼び出しだ。
引数が `:Integer` なので `Integer` メソッドをオブジェクト化したものを返す。

この `method( )` にはレシーバーが書かれていない。ということは〈その場における `self`〉がレシーバーとなる。
つまり，実際に何がレシーバーになるかは書かれた場所によるのである。
でもそれで構わない。`Integer` メソッドは `Kernel` に定義されており，どんなオブジェクトに対しても使えるうえ，そもそもレシーバーによらず同じ動作をする。

ここまで分かったら，つぎは

```rb
map(&method(:Integer))
```

を検討しよう。
`&` のあとに `Proc` オブジェクトではなく `Method` オブジェクトが続いている。
こういう場合，シンボルを与えたときと同様，`to_proc` メソッドが暗黙に呼ばれることになっている。（参照：[Method#to_proc](https://docs.ruby-lang.org/ja/2.6.0/method/Method/i/to_proc.html)）

結果，これは

```rb
map{ |x| self.Integer(x) }
```

みたいなものなわけだ（`self.` はあえて書いておいたが，もちろん省略できる）。
これで理解できた。

### これは良いものなのか？

「でもよ，この書き方って，大して短くねえよな？」

確かに

```rb
# (a) ブロック版
integer_strings.map{ |str| Integer(str) }

# (b) Method 版
integer_strings.map(&method(:Integer))
```

と並べて比べても，**字数では** 3 字くらいしか短くなっていない。ブロックパラメーターの名前を長くすればもっと差が出るが，逆にブロックパラメーターが `x` とかだったりすると，かえって長くなる。

では論理的に簡潔かというと，ブロックパラメーターを書かなくてよいという意味では確かに簡素化されて思考の経済になっているが，〈`method` メソッドの呼び出し〉という別のものが入り込んで，**帳消しになっている**感が否めない（笑）[^mpb]。

[^mpb]: ブロックが 2 個以上のブロックパラメーターを持つ場合は `Method` 版の利点が増すだろう。

こういうところもまた，`Method` オブジェクトの出番が少ない原因の一つだろう。


## Rails のビューで

この節で挙げる例は，Ruby on Rails のものなので，Rails やってない人はごめんなさい。

`User` モデルに `name` というカラムがあったとする。
ユーザー一覧とか，特定のユーザーの詳細情報を表示する画面には，当然「`氏名`」といった文字列を表示することになる。

しかし，ビューファイルに直に「`氏名`」などと書くことは勧められない。
そのままでは多言語化できないし，あとで「`姓名`」に変えたくなったとき，あちこちのビューを書き換えることになる。

Rails には i18n の仕組みが備わっており，〈`User` モデルの `name` カラムの日本語での名前は「`氏名`」である〉といったことを一箇所で指定しておけば，ビューでその文字列を呼び出すことができる。

呼び出し方は複数あるが，一つの方法は

```rb
User.human_attribute_name(:name)
```

のように，モデルの `human_attribute_name` メソッドを使うこと。
「human」というのは〈人間に見せるための〉という含みだろう。

それにしてもメソッド名が長い。
このままの形だとビューがゴチャゴチャする。`User` モデルにはほかにも `email`, `phone`, `company`, `address` などなどのカラムがあるだろうから，ビューが `User.human_attribute_name` だらけになる。

そこで，だ。ビューファイルの先頭に

```erb:erb
<% han = User.method(:human_attribute_name) %>
```

とか

```slim:Slim
- han = User.method(:human_attribute_name)
```

などと書いておけば

```rb
han[:name]
```

の形で使うことができ，ビューがスッキリする。
（これが良い流儀といえるかどうかは知らん）

この例は，〈固定したレシーバーとさまざまな引数に基づいて動作するメソッドを何度も呼び出す〉ケースに該当すると言えるだろう。

# さいごに

* `Method` オブジェクトの出番はやっぱり `Proc` オブジェクトより少ない
* その原因は，レシーバーが固定であることや，メソッド呼び出しに使っても大して簡潔にならないことなどだろう
* 現実に使いうると言えそうな例を二つ挙げた
* それぞれ，レシーバーにとくに意味が無い例と，レシーバーに依って動作する例

# 追記：メソッド参照演算子（2019-06-11）

コメントで @asm さんが教えてくださったとおり，Ruby 2.7 で導入が予定されているメソッド参照演算子 `.:` を使えば，

```rb
User.method(:human_attribute_name)
```

は

```rb
User.:human_attribute_name
```

と書ける。
同様に，

```rb
method(:Integer)
```

は

```rb
self.:Integer
```

と書ける。この `self` は略せない。
なお，この　`Integer` は `Kernel` モジュールのモジュール関数なのでレシーバーは `Kernel` にすることもできて，

```rb
Kernel.:Integer
```

と書くこともできる（コメントのとおり）。

rbenv の人は `rbenv install 2.7.0-dev` して試すことができる。

メソッド参照演算子については @hanachin_ さんが記事を書かれていた：
[Ruby 2.7の新機能メソッド参照演算子 - Qiita](https://qiita.com/hanachin_/items/1aa1ba38a87dee91aed6)
（読んだことを忘れていた・・・ orz）

# 追記：メソッド参照演算子はボツ（2019-12-03）

結局，メソッド参照演算子 `.:` は Ruby 2.7 に取り入れられなかった。幻に終わったのだ。

2.7.0-preview3 で既に消えている。

# 追記：番号指定パラメーターの出現でますます疎遠に（2019-12-25）

Ruby 2.7 では「番号指定パラメーター」と呼ばれるブロックパラメーターが導入される。
これは，ブロック内でブロックパラメーターを宣言せずに `_1` とか `_2` などと書けば，1 番目，2 番目のブロックパラメーターになる，というもの。

これを使えば

```rb
integer_strings.map{ |str| Integer(str) }
```

は

```rb
integer_strings.map{ Integer(_1) }
```

と書ける。
どう見ても

```rb
integer_strings.map(&method(:Integer))
```

より簡潔だし理解しやすい。
これで Method オブジェクトの出番はますます減るだろう。