はじめに
Railsを用いた開発を進めている中で、ブロックを渡すはずのメソッドにシンボル形式のメソッドを渡している処理を見かけました。
今までも同様の記述は見たことがあったのですが、なぜシンボル形式のメソッドを使用するのかよく理解していなかったので、この機会にメソッド・Symbol・Procについて調べてみました。
Enumerable#collectを使用した例
Rubyリファレンスマニュアルには下記のようなcollectメソッドの使用例が記載されています。
p (1..3).collect { "cat" } # => ["cat", "cat", "cat"]
各要素に対してブロックを評価した結果を全て含む配列を返します。
上記の例だと(1..3)
の各要素に対して{ "cat" }
を適用するため、戻り値が["cat", "cat", "cat"]
となるわけです。
一方で、ブロックの記法を用いず、シンボル形式のメソッドを渡す書き方もあります。
p (1..3).collect(&:to_s) # => ["1", "2", "3"]
本来ならばcollectメソッドにはブロックを渡さなければいけないにも関わらず、上記の例ではシンボルを渡しているように見えます。
そこで、
ブロックを渡すはずのメソッドになぜシンボル形式のメソッドを渡すことができるのか
メソッドをシンボルの形で渡すのはなぜなのか
という2点に着目して調査をしてみました。
to_procメソッドとの出会い
調査を続けていると、一つのメソッドに遭遇しました。
それがto_proc
メソッドです。
Rubyリファレンスマニュアルには下記のように説明されています。
self に対応する Proc オブジェクトを返します。
生成される Proc オブジェクトを呼びだす(Proc#call)と、 Proc#callの第一引数をレシーバとして、 self という名前のメソッドを残りの引数を渡して呼びだします。
生成される Proc オブジェクトは lambda です。
下記のように使用します。
# 明示的に呼ぶ例
:to_i.to_proc["ff", 16] # => 255 ← "ff".to_i(16)と同じ
# 暗示的に呼ぶ例
# メソッドに & とともにシンボルを渡すと
# to_proc が呼ばれて Proc 化され、
# それがブロックとして渡される。
(1..3).collect(&:to_s) # => ["1", "2", "3"]
(1..3).select(&:odd?) # => [1, 3]
つまり...
- Symbolで記述されたメソッドはProcオブジェクトに変換可能である。
- 引数に&とともにSymbolを渡す事で、メソッドをブロックとして渡すことができる。
ということがわかりました。
実験
ここまでの内容を噛みくだいて理解するため、下記のようなサンプルコードでto_s
を色々な形で出力してみました。
puts to_s.class
# => String
# to_sメソッドの戻り値のクラスを調べている.
puts :to_s.class
# => Symbol
# to_sメソッドの情報を持ったSymbol
puts :to_s.to_proc.class
# => Proc
# to_sメソッドをブロックとして持つProcオブジェクト
puts :to_s.to_proc[100].class
# => String
# Procを実行した結果の戻り値なのでString(今回だと'100')が返される
単にto_s
メソッドを呼び出した場合はメソッドの戻り値であるStringオブジェクトが返ってきます(当たり前ですが...)。
一方で:to_s
や:to_s.to_proc
では、それぞれSymbolオブジェクトやProcオブジェクトを返しています。
to_s
の実行結果の戻り値ではなくto_s
メソッドをSymbolやProcの形式で扱うことができていることがわかります。
また、興味本位でメソッド定義部分自体のクラスも調べてみました。
puts def hoge
'hoge'
end.class
# => Symbol
この段階ですでにメソッドはSymbolオブジェクトになっているのですね。
まとめ
前述の疑問点に対して私が出した答えは以下です。
ブロックを渡すはずのメソッドになぜシンボル形式のメソッドを渡すことができるのか
- シンボルは
to_proc
メソッドを持っているため、シンボルが持つメソッドの情報をProcオブジェクトに変換してブロックを実行することが可能である。
メソッドをシンボルの形で渡すのはなぜなのか
- メソッドを&とともにシンボルの形で引数に渡すことにより、メソッドの戻り値ではなくブロックを渡すことができる。
参考