&演算子はProcオブジェクトをブロックへ変換し、ブロックをProcオブジェクトへ変換します。この場合、&はシンボル :+ をブロックへ変換しようとします。ブロックへの変換にはRuby組み込みの型変換が利用されます。ブロックへの型変換ではまず、Procオブジェクトが与えられているかをチェックします。ブロックが与えられていなければ、引数として与えられたオブジェクトをProcへと変換すべく to_procメソッドが呼び出されます。このとき、シンボル :+ に to_proc メソッドが定義されていれば、それが呼び出されます。Ruby 1.9では、Symbol#to_roc が定義されています。このto_procメソッドは、Procオブジェクトを返します。

つまり、 &:+ は { |x, y| x + y } になるということです。

での目玉機能といえばスレッド機構の変更やFiber (source) だが、これ以外にも標準ライブラリに加えられた便利な機能がある。Object#tap (source) については既に紹介した通りだ。tapメソッドはメソッドコールのチェインの途中に処理を割り込ませられる便利な機能だ。tapメソッドがそうであったように、メソッドのアイデアも、少し前からRubyコミュニティでは知られていた。to_proc相当の機能は、Ruby 1.9より前のバージョンでも利用できたのだ。Ruby 1.9 はこの機能をそのままSymbolクラスへと統合した。このおかげで、#to_procを使うのにライブラリは必要なくなった。Reg Braithwaite による手短なSymbol#to_procの紹介記事では (1..100).inject(&:+) のようなコードが動くようになったと説明 (source) されている。

この振る舞いのおかげで次のようなコードを書けるようになる。

plus = :+.to_proc

puts plus.call(1,2) # '3'が表示される

Symbolクラスにto_procメソッドがあるということは、このやり方があらゆるシンボルに通用するということだ。



to_s = :to_s.to_proc

to_s.call(42) # 文字列"42"が返ってくる



to_procの実装はシンプルだ。Dave Thomas(PragDave)はその動作(source)をこう説明している。

def to_proc

proc { |obj, *args| obj.send(self, *args) }

end

to_procメソッドはProcを生成します。生成されたProcは、オブジェクトを引数にして呼び出されると、渡されたオブジェクトに対してsendメソッドを呼び出します。sendメソッドの第1引数のシンボルは、to_procのレシーバの自分自身です。ですから、たとえば names.map(&:upcase)の場合、namesに格納されている字列をイテレートするわけですが、その際にはnamesの各要素に対してupcaseメソッドを呼び出すブロックがコールバックされます。





この機能のおかげでコードがとても簡潔になるのだが、ひとつ疑問が残る。つまり、これでコードは読みやすくなるのだろうか？これに対する答えはたぶん to_proc がRuby 1.9の標準となったことで変わっていくだろう。なぜなら、いまやSymbolにはかならずto_procメソッドがあるからだ。以前であれば、Symbol#to_procを実現しようとすれば、自分でコードを書くか、Symbolを再オープンしてto_procメソッドを追加するライブラリを使うしかなった。さらに、こうしたコードを理解するにはSymbol#to_procというイディオムを知っていなければならなかった。これについてはRuby 1.9で公式にサポートされたことで、今後はドキュメントでも言及されていくようになるだろう。



それでもやはり疑問は残る。確かに、

(1..100).map(&:to_s)

は

(1..100).map{|x| x.to_s }

よりも短く書ける(この例では5文字短かい)。しかし、これは複雑さを増すに見合ったものだろうか？ それともやりすぎだろうか？



Ruby 1.9のProcには他にも追加された機能がある。Porc#curry がそうだ(といってもsvnリポジトリのごく最近のリビジョン(r15459以降)でなければ使えないが)。ruby-core メーリングリストの議論によれば、Proc#curry の機能(source)とは次のようなものだ。



全然難しいことはなくて、

proc {|x, y, z| x + y + z }.curry

は、次のようなprocを返すのと同じです

proc {|x| proc {|y| proc {|z| x + y + z } } }

カリー化（currying）とは、計算機科学分野の技法の一つ。複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること。

つまりカリー化とは、n個の引数を取るProcを1つの引数だけで呼ぶことができるということだ。この場合は(当たり前だが)処理の結果を返すことはできない――処理を実行するために必要な引数が与えられていないからだ。よって、処理結果の代わりに、(n -1)個の引数をとる新しいProcが返される。これが繰り返されて、Procのコードを処理するのに必要なだけの引数が揃ったら、Procは処理を実行してその結果を返す。



ruby-core メーリングリストの議論スレッドから利用例を紹介しよう。

plus_five = proc { |x,y,z| x + y + z }.curry.call(2).call(3)

plus_five[10] #=> 15

補足: plus_five は Procであり、[]演算子はProcではProcを実行するようにオーバーライドされている。



注意: Proc#curry が Ruby 1.9 に追加されたのはつい最近である。試してみるには、最新のsvnリビジョン(r15459以降)を利用しなければならない。