uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

GroovyユーザのためのRubyのクロージャ対応物(block/Proc/lambda)の理解法

  • Rubyのlambdaはgroovyクロージャと同等*1
  • RubyのblockとProcは実体は同一であり、つまりいずれもProc。違いは、受け取る側で、明示的にProc型引数として受け取る(Proc)か、暗黙の唯一のブロック引数として受け取る(ブロック)か、という受け取る側の表記の問題にすぎない*2。つまりblockというのは構文上のみの存在と考えることができる。yileldは、暗黙に受け取った場合に、明示的にcallできないのでそれをするための特別な構文。
  • RubyのblockもしくはProcには、クロージャを文法を拡張する手段として使う場合に優れた特徴がある。それは、returnがクロージャからのリターンではなく、Procやブロックリテラルが定義された場所がメソッド中ならそのメソッドからのリターンになるということ。いわば、PC(プログラムカウンタ)がレキシカルスコープで引き継がれるということ*3

整理としては、高階関数やアルゴリズムとして使うにはlambdaを、イテレータとかloanパターンとして、つまり制御構造の拡張として使う場合には専用のblock/Procを、ということでいいかな。

ちょっとだけ感想を言うと、一級市民としてのクロージャの扱い方は、実装としても*4言語仕様(文法+意味の両方において)としても、Groovyのほうがはるかにすっきりしているね。もちろんGroovyのそれにも不満がないわけではないけれども。

*1:ただしdelegateはない。ちなみにGroovyクロージャにおけるスコープ制御はたぶんRubyよりも柔軟で、クロージャインスタンスごとに[http://groovy.codehaus.org/api/groovy/lang/Closure.html#getResolveStrategy():title=スコープ解決戦略]を切り替えることができ、それをOWNER_FIRST/ONLYとすることでレキシカルスコープ、delegateで積極的に変えてDELEGATE_FIRST/ONLYにいく場合は動的スコープとすることなどができる。

*2:Procの生成・リテラルの書き方もいくつかあるけど、blockとProcを書き分けることにはならないんじゃないかな。

*3:あるいは、「スタックフレーム」という暗黙変数がレキシカルスコープで引き継がれる、か。今いち良い言い方にならないな。

*4:少なくともjrubyの実装を見る限りにおいては。