クロージャによる超軽量並行プロセス

sumiiさんの『クロージャによる超軽量並行プロセス』を、jijixiさんがRubyに移植したようなので、試してみました。jijixiさんが書いたサンプルを写経して(そのあと、変数名などを自分の理解した範囲で書き換えて)やっと理解しました。

require 'picalc'

# Syntax sugar
class Chan
  def <<(arg)
    send(self, arg)
  end
  def >>(arg)
    recv(self, arg)
  end
end

# Make ready for server.
server_channel = Chan.new
server_proc = Proc.new {
  server_channel >> Proc.new{|x, response_channel|
    y = x * x # Service
    response_channel << y
    server_proc.call
  }
}
server_proc.call

# Use the server.
p_proc = Proc.new{|x| p x}
result_channel = Chan.new
server_channel << [123, result_channel]
result_channel >> p_proc

実行結果は15129です。server_channelに123を送り込み、その二乗(15129)を結果としてもらって表示しています。
うーむむむ。この頭の使い方は記憶に残っているぞ。継続とか遅延評価をやったときの頭の使い方だ。「クロージャのキャッチボール」というか、「クロージャの雪合戦」というか。
次に、同じようにフィボナッチサーバも写経+書き換えしてみました。

require 'picalc'

# Syntax sugar
class Chan
  def <<(arg)
    send(self, arg)
  end
  def >>(arg)
    recv(self, arg)
  end
end

# Fibonacci server.
fib_channel = Chan.new
fib_proc = Proc.new {
  fib_channel >> Proc.new{|n, response_channel|
    fib_proc.call # (A)
    if n <= 1
      response_channel << n
    else
      son_channel = Chan.new
      daughter_channel = Chan.new
      fib_channel << [n - 1, son_channel]
      fib_channel << [n - 2, daughter_channel]
      son_channel >> Proc.new{|x|
        daughter_channel >> Proc.new{|y|
          z = x + y
          response_channel << z
        }
      }
    end
    # (B)
  }
}
fib_proc.call

n = 10
result_channel = Chan.new
fib_channel << [n, result_channel]
result_channel >> Proc.new{|x| printf("fib(#{n}) = %d\n", x) }

疑問:(A)の場所にあるfib_proc.callはここになければいけないのかな?(B)でも動いたが…。うーん、まだ理解していないようだ。
→bellbindさんから論文を紹介して貰いました。感謝。Event-Based Programming without Inversion of Control
追記:
『『クロージャによる超軽量並行プロセス』を Ruby で』をもう少し Ruby っぽくで、<< と >> のメソッドになってた。わーい、うれしい♪