今回は、11章〜12章を読みました。いやはや、どうも理解が追いつきません。何がしたいのか、その実装で何ができるようになったのか、という点はまあだいたい分かります。しかし、なぜその実装でできるのか、という肝心の点が分からないわけです。むむむ。
11章 並行性の高水準な抽象化
8章あたりからずっと扱っている Async についてさらに機能追加し、この章で Async については一段落です。ページ数が少ない章ですが、race
と concurrently
という有益そうな関数が出てきます。どちらの関数も、ふたつの IO アクションを並行に実行し、どちらか一方のアクションが例外で失敗したらもう一方のアクションが中断するようになっています。race
は片方のアクションが結果を返したら終わり、concurrently
は両方のアクションが結果を返したら終わりです。
まあそういう関数だというのは分かるとして、concurrently
の実装に使われている waitBoth
の実装コードが難しい。
waitBoth :: Async a -> Async b -> IO (a,b) waitBoth a1 a2 = atomically $ do r1 <- waitSTM a1 `orElse` (do waitSTM a2; retry) r2 <- waitSTM a2 return (r1,r2)
わからないのが orElse
の後のコードで waitSTM a2
を呼ぶところ。例外を投げるか検査するのが目的、っていうけど、そのために a2 を実行するの? それで、retry
で結果を捨ててまた a2 を実行する? 無駄じゃない? という話になりました。後で振り返ると、retry について勘違いしてたのかも、という感じですが。
多分、retry っていう言葉が悪いよね。Haskell は面白いけど、出てくる用語のセンスがなんかいちいちおかしい気がする。
12章 並行ネットワークサーバ
だいぶ具体的な例が出てきました。
まずは簡単なサーバ。まあこれは難しくない。単純なソケットの読み書き。クライアントからのメッセージ送信を受けてサーバがメッセージを返す。クライアントからの接続ごとにスレッドを生成して処理する。定番ですね。
次に、それの拡張。クライアントがサーバにコマンドを送ることで、サーバの応答が変化する。このとき、他のすべてのクライアントにも同じ変化が起こるようにする。このためには、サーバが状態を持つ必要があり、しかもそれを各スレッドが共有しなくてはならない。いくつかの設計案を検討した後、STM が良いという話になり、実装コードが示されます。この実装を理解するのが意外と骨が折れる。うーん、これは10章の STM をもう一度復習しておいたほうがいいかもしれない。
最後にチャットサーバ。だいぶ機能が増えていてコードを読むのが大変になっていますが、基本的にはこれまでやったことの延長。ということは逆に、これまでやったことが分かってないとダメですが・・・。
次回
次回は、12章をもう一度読みつつ、ところどころに出てくる読者への課題に取り組んでみます。