In message "[ruby-dev:19798] Re: (1.8.0-preview2) Proc#call"
    on 03/03/12, Masatoshi SEKI <m_seki / mva.biglobe.ne.jp> writes:
|
|
|咳といいます。
|
|> そのようですね。breakで終了したのか正常終了したのか知る方法
|> が欲しいというニーズに答えるためのようですね。うーむ、難しい。
|> かなりややこしいので咳さんと直接話さないといけないかなあ。
|> どうやって話すかが問題だけど。
|
|むむむ。まつもとさんが関東へ来るようなイベントないですかねえ。
|合わせて咳も関東へ行きますよ。

5月(17日?)にアスキーがイベントを行うって行ってました。16日
には情報処理学会のHI研究会ってのがあるんで東京に居ます。
でもちょっと先ですね。

もっと先だと7月にオープンソースカンファレンス(7日から11日、
オレゴン州ポートランド) に出席するんで、東京を経由するんじゃ
ないかと。

|ruby-bugs-ja PR#98というのはどこで見られるのでしたっけ。

  http://www.ruby-lang.org/cgi-bin/ruby-bugs-ja/

から1.7グループに入ってください。

一応、現状を簡単にまとめると

  * local jumpにはreturn, next, break, redo, retryがある。

  * orphan(孤児)な手続きオブジェクトからのlocal jumpは例外を
    発生させることがある。

  * orphanとは手続きオブジェクトを作るモトになったブロックを
    囲むメソッドが終了してしまっている手続きオブジェクトであ
    る。

      def foo
        f = lambda{|x| p x}
        p f            # fはまだorphanじゃない
        return f
      end
      f2 = foo()       # f2はfooが終了しているのでorphan

  * returnはブロックを囲むメソッドの末尾にジャンプするため、
    orphanな手続きオブジェクトから呼び出されると飛び先が分か
    らずlocal jump errorになる。

      def foo
         lambda{|x| return x}
      end
      foo().call(42)

  * nextはブロックの実行を中断すし、yield(またはProc#call)が
    終了する。1.7ではnextは値を取ることができ、その値はyield 
    の戻り値となる。nextはlocal jump errorにならない。

  * breakはブロックが与えられているメソッドの実行を中断する。
    1.7ではbreakは値を取ることができ、その値はメソッドの戻り
    値となる。ブロックが手続きオブジェクトとなった場合、その
    内部でのbreakの挙動には検討の余地がある。※

  * redoはブロックの実行の先頭に戻る。redoはlocal jump error
    にならない。

  * retryはブロックが与えられているメソッドの実行をその引数
    の評価からやり直す。orphanな手続きオブジェクトから呼び出
    されると飛び先が分からずlocal jump errorになる。

      f = lambda{retry}
      f.call

さて、ここにorphanな手続きオブジェクトpがあるとします。
このpの実行の仕方は3種類あります。

  (1) ブロック引数(&p)を使う
  (2) callメソッドを使う
  (3) yieldメソッドを使う

これらそれぞれの場合、あるべきlocal jumpの挙動はどうあるべき
かを考察します(ただし、nextとredoについては考察の必要はあり
ません。不確定な要素がないからです)。

1のブロック引数を使う場合、コードは次のようになります。

  5.each(&p)

これは本来pが持つブロックをeachメソッドに渡したのと同じ動作
をするべきだと考えます。よって、pの中のbreakはeachの実行を中
断し、retryはeachの実行を最初からやり直しするべきだと考えま
す。では、returnはどうでしょう。選択肢は二つあって、ひとつは
「pが持つブロックをeachメソッドに渡したのと同じ動作」という
部分を厳密に考えてeachメソッドがreturnするという挙動、もうひ
とつは、return文と返るべきメソッドが遠く離れているのは変だと
いうことで、エラーにする挙動。現在の1.8は後者を採用していま
す。

2のcallメソッドを使う場合、コードは次のようになります。

  p.call

この場合、ブロックを実行しているメソッドはcallメソッドになる
と解釈するのが自然のような気がします。そうすると可能な選択肢
はそれぞれ
                     A           B
  break         callを中断      例外
  return        例外            例外
  retry         callを再実行    例外

です。1.6はBBB、現状の1.8はABBです。使い勝手から考えると
returnの例外は決まりのような気がしますが、retryの例外は考慮
の余地があるかもしれません。ABAとかも面白そう。

3のyieldメソッドですが、これは1.7から導入されたものです。使
い方はcallと同じです。

  p.yield

もともとyieldはcallで行われているパラメータの数の厳密なチェッ
クを回避することが目的で、挙動の違いもそれだけでした。しかし、
ruby-bugs-ja PR#98の過程でbreakを例外にするかどうかという違
いも付与されるようになりました。でも、今改めて考えると一つの
メソッドに複数の目的を付与するのはやっぱり良くないようにも思
います。もし、可能であればlocal jumpについては上記のABBまた
はABAでcallと揃えた方が良いと思います。

さて、となると最終的に考慮しなければならない問題は、dRubyが
ネットワークを介してブロックを実行するときbreakやretryを検出
しなければならない時、最低限なにが必要か、という点です。

田中哲さんが以前に例外に依存しない方法を提案していらっしゃっ
たと思うのですが、もう忘れちゃったなあ。

                                まつもと ゆきひろ /:|)