π-calculus 超入門

π-calculus は、80 年代の終わりごろに Milner らによって提案された並行計算のモデルの一つです。そこでは、プロセスと呼ばれる複数の独立した主体が、通信チャネルと呼ばれるデータの通り道を介して値をやりとりしながら、計算を行っていきます。π-calculus にはいろいろな変種があるのですが、ここではとりあえず次のような構成要素からなるものを考えましょう。

new x . P 新しいチャネル x を作ってから、プロセス P を実行する (channel creation) x![v 1 , ..., v n ] チャネル x に値 v 1 , ..., v n を送る (asynchronous output) x?[v 1 , ..., v n ] . P チャネル x から値 v 1 , ..., v n を受け取って、P を実行する (input guard) P | Q プロセス P とプロセス Q を並行して実行する (parallel composition)

たとえば

x![123] | (x?[v] . result![v])

result![123]

π-calculus では、チャネル自体を値として送受信することもできます。たとえば、

(new c . (x![c] | (c?[v] . print![v]))) | (x?[d] . d!["abc"])

(c?[v] . print![v]) | c!["abc"]

print!["abc"]

π-calculus の最大の特徴は、この「チャネル自体も値としてやりとりできる」という点にあります。この考え方をさらに押し進めていくと、実は整数や文字列などの値も、チャネルとその上での通信だけで表せてしまうことが知られています。つまり、λ-calculus ですべてのものがλ式として表されているのと同様に、π-calculus ではすべてのものをプロセスで表す（encode する）ことができるのです。

たとえば bool 値をプロセスで表してみるとどうなるでしょうか。データ型としての bool は、「true」と「false」の二つの定数で構成されます。そこで「true」をあらわすプロセスとしては、二つのチャネル t, f を受け取り、t のほうに空の値を送信する

true?[t,f] . t![]

false?[t,f] . f![]

new t . new f . (x![t,f] | (t?[] . P) | (f?[] . Q))

ただし、このままだとちょっとまずいことが起きます。上のように true や false を定義しても、これらは一回値が来ると消えてなくなってしまいますから、何回も使うためにはそれだけの数のプロセスを用意しておかないといけません。これでは使われる回数があらかじめわからない場合困ってしまいます。そのため、一般的な π-calculus には、replication と呼ばれる無限個の複製を作る operator が存在します。

!P P | P | P | P | P | ... のように無限個の P が並行して存在する (replication)

記号が output と同じ ! なのでちょっと紛らわしいのですが、replication を使えば、先の true と false も

!(true?[t,f] . t![]) | !(false?[t,f] . f![])

!(fact?[n,r] . (if n=0 then r![1] else (new c . (fact![n-1,c] | (c?[x] . r![n * x])))))

!(fib?[n,r] . (if n<2 then r![1] else (new c . new d . (fib![n-1,c] | fib![n-2,d] | (c?[x] . d?[y] . r![x+y])))))

もちろん = や + などの整数演算は、また別に定義しておかないといけません。余裕のある人は自分で考えてみると良い練習になると思います（実は λ-calculus 全体が π-calculus の上で自然に encode できるので、bool や int が表せるのは当たり前なのですが）。

文献リスト

sumii@is.s.u-tokyo.ac.jp