2008-02-08
■ [ruby] Fiber#transferについて調べてみた
- Fiber::Core は callcc 並の黒魔術である、とレッテルを貼って使わせない
[[ruby-dev:31596] Re: Fiber reviesedより引用]
黒魔術と聞いてやってきました。
rubyのソースコードのtest/ruby/test_fiber.rbに例があるんですが、これが難しい…。
def test_transfer ary = [] f2 = nil f1 = Fiber.new{ ary << f2.transfer(:foo) :ok } f2 = Fiber.new{ ary << f1.transfer(:baz) :ng } assert_equal(:ok, f1.transfer) assert_equal([:baz], ary) end
とりあえず、Fiberについてのポイントを3つ。
- Fiber.newしただけではまだ実行されない。
- Fiber#resumeで実行。実行が終わるか例外が発生すると実行元に戻る。
- Fiber#transferというのもあって、こっちは実行が終わるか例外が発生するとroot fiber(メインスレッドみたいなもの?)に戻る。
んで、resumeは普通だがtransferは黒魔術らしいw
では、上のソースを読んでみるよ。test_transferを実行すると、
- ary = [], f1 = Fiber, f2 = Fiber のように代入される
- f1.transfer()が呼ばれ、f1に制御が移る
- f2.transfer(:foo)が呼ばれ、f2に制御が移る (このとき、f1の表す仕事は「値をaryに突っ込み、:okを返す」)
- f1.transfer(:baz)が呼ばれ、f1に制御が移る (このとき、f2の表す仕事は「値をaryに突っ込み、:ngを返す」)
- f1が再開される。渡された値 :baz がaryに突っ込まれ、:ok が返り値になる
- f1の実行が終わる。resumeならここでf2に戻るはずだが、transferを使ってるので、root fiberに戻る
- assert_equal(:ok, :ok) => テスト成功。やったね
ふむふむ。なんつーか、この実行順序、無茶だよね(笑)。強制的に親元に連れ戻される家出少女のような(意味不明)。
しかし、まだtransferが黒魔術な理由はよくわかってない。 なんとなく、ヤバそうな雰囲気は感じるんだけど。 処理系のスタックの知識があればきっと瞬時に「これがこうなって、こうなったときに危ない」とかイメージできるんだろうなー。
resume でも transfer でも片方だけ使うのはそれほど問題ないんですが、両方同時に使うとカオスになるんです。<br>で、どちらか選ぶなら resume の方がきっといいので、transfer にネガティブなレッテルを貼ってしまえと。<br>ただ callcc の黒魔術と違って、transfer を便利だと思えるような例は思いつかないんですよねえ。
状態遷移とかになんか使えないのかなあとかは考えた。<br><br>あと、transfer の状態で例外あがったらどうするのん、とか、そういうのが黒い。
> 両方同時に使うとカオスになるんです。<br>なるほど、了解です。<br><br>僕もまだtransferの使い方は思いつけてないです。難しい。