full-sessionsの新バージョン 0.6.1 を HackageDBにアップロード しました。

full-sessions は、Haskellにおけるマルチスレッド/ネットワークプログラミングのライブラリです。 Sessionモナドという新しいモナドを使ってプログラムを書きます。 チャネルの型表現にπ計算の型理論の一つであるセッション型を採用しています。セッション型のおかげで、チャネルの「使われ方（プロトコル）」がスレッド間で整合していることを実行前の型検査によりチェックできます。これにより、ある種のデッドロックや型の不整合は型検査で排除できるようになります。 さらにHaskellの型推論でセッション型を推論させれば、プログラムがチャネルを意図通りに使っていることを型表現からチェックできます。

full-sessions は、HList等で見られるような型レベルプログラミングのテクニックを駆使したかなり実験的なライブラリで、インタフェースすら安定していませんが、Haskellで作れる「型付きの」DSLの例としても、大きな意味があると勝手に考えています。

今回のバージョンは、

配布バイナリの examples/ 以下に、簡単なチャットプログラムと、SMTPクライアントのexampleがあります。

今のところ、まともなドキュメントを書く気力がありませんが、セッション型によるプログラミングの雰囲気だけでもわかっていただければ。

単純な型推論の例 文字列をチャネルで送出する Main > : t typecheck1 $ \ c -> send c "abc" typecheck1 $ \ c -> send c "abc" :: (Length ss l) => Session t (ss :> Send [Char] a) (ss :> a) () 型 Session t ss tt a は indexed monad とうモナドの一種です。 チャネル(の集合)ssをttに変換する、とか、事前条件ssで実行すると事後にttとなる、と読みます。(ss :> s) は型レベルのリストで、ssの末尾にsを加えたリストを表現しますす。

例えば Session t (ss :> Send [Char] a) (ss :> a) () という型をもつ上のスレッドは、あるチャネルのセッション型を Send [Char] a から a に変換します。つまり、スレッドはそのチャネルを 1度 文字列を送るために使う、ということが型からわかります。 また、他のチャネルは使われないということもわかります。 整数を受け取った後にインクリメントして送り返し、その後()を送る Main > : t typecheck1 $ \ c -> recv c >>>= \ x -> send c (x + 1 ) >>> send c () typecheck1 $ \ c -> recv c >>>= \ x -> send c (x + 1 ) >>> send c () :: (Length ss l, Num a) => Session t (ss :> Recv a (Send a (Send () a1))) (ss :> a1) () チャネルcとdから受け取った和をチャネルcに返す Main > : t typecheck2 $ \ c d -> recv c >>>= \ x -> recv d >>>= \ y -> send c (x + y) typecheck2 $ \ c d -> recv c >>>= \ x -> recv d >>>= \ y -> send c (x + y) :: (Num a, Length ss l) => Session t ((ss :> Recv a u) :> Recv a (Send a a1)) ((ss :> u) :> a1) () 見た目、型変数がたくさんあってややわかりづいらいですが、dのセッション型がRecv a uからuに、cのセッション型がRecv a (Send a a1)からa1に変換されます。

つまり dは型aの値を1度受信する。 cは型aの値を1度受信し、1度送信する ということが型からわかります。チャネル間のデータの流れはセッション型からはわかりません。 チャネル生成とスレッド生成 new というプリミティブでチャネルを生成し、 forkIOs というプリミティブで新しいスレッドを走らせることができます。

new はこんな型。 セッション型Botをもつチャネルをひとつ環境に追加します。 Main > : t new new :: (Length ss l) => Session t ss (ss :> Bot) (Channel t l) ixdo記法と組み合わせて、普通のdo記法っぽい記法が使えるようになります。(rebindable syntax でもいいかも) ixdo記法は cabal install ixdoppして、{-# OPTIONS_GHC -F -pgmF ixdopp #-} をソースの最初に書けば使えます。 test = ixdo c <- new forkIOs (send c "abc" ) x <- recv c io (putStrLn x) io は IOモナドのアクションを実行するためのコマンド。 Main > : t test test : :: (Length ss' l, Ended l ss) => Session t ss' (ss' :> End) () Main > test abc * Main >

エラーの例 セッション型は、チャネルはちょうど2つのスレッドのみ所有できます。2つのスレッドにおいてチャネルの使われ方が整合しないと型エラーになります。

例えば、次のように、 2つのプロセスが同じチャネルに同時に送信/受信するような場合は型エラーとして発見できます。 Main > new >>>= \ c -> forkIOs (send c "123" ) >>> send c "123" Top level : Couldn't match expected type `Send [Char] a' against inferred type `Recv t' u'' When using functional dependencies to combine Comp (Send t u) (Recv t' u') Bot, arising from the dependency `s u -> t' in the instance declaration at src / FullSession / TypeAlgebra.hs : 44 : 9 Comp (Send [Char] a) (Send [Char] a) Bot, arising from a use of `forkIOs' at < interactive >: 1 : 15 - 36 本来 チャネルで受信すべきところを送信しているため、 Couldn't match expected type `Send [Char] a' against inferred type `Recv t' u'' というエラーがでています。