やりすぎIOモナド

とある本からの受け売りだけど、漠然とIOモナドってやりすぎだよな、と思っていたところがはっきりしたのでメモ。IOモナドには次のような性質がある。

  • プログラム中の(実行を期待している)全てのIOモナドは結局全順序に順序付けられる
  • ある関数の返り値にならなければIOモナドは実行されない

どちらも、副作用を含むIO処理を取り扱いながら参照透明性を保つために安全側に倒した結果なんだろうな。ところが、これがIOモナドの扱いをちょー面倒にさせる。
前者のせいで、本来順序付けしなくていい独立したIO処理も順番にしか処理できなくなる。ネットワークからデータ取得時とか、アクセスした回数分だけきっちり遅延をもらってしまう。Rubyとかだと、ネットワークアクセスをスレッドに分割すると遅延をごまかしてスループットを上げることができるが、そういうことは不可能*1
後者のせいで、いわゆるprintfデバッグが不可能になる。IOに関係ない関数にデバッグのためにIOモナドを追加して、実行(出力)させようとすると、その「ある関数」の型を(IOモナドを返り値に持つように)変更する必要がある。型推論と前者の性質のおかげで、その型の変更はプログラム全体に波及し…、この先はちょっと想像したくない。
これって、Concurrent Cleanが使ってるような一意型だと解決できたりするのかなあ?前者だと、全順序じゃなくて半順序にはできそうな気がする。後者のほうは…これはしょうがないのかなあ。副作用と型の永遠のジレンマか?

追記 Concurrent Cleanの場合

丁度 更新されたようなので、id:lethvertさんのClean入門のファイル入出力の解説(http://www.geocities.jp/lethevert/softwares/clean/gettingStarted20.html)をざっと見てみた。
うーん、やっぱり「順序を保持しないといけない部分には同じファイルハンドル(相当のもの)を一意に受け渡す」というこっちの仕組みのほうが入出力としては素直な気がする。モナド自体のメタパターン性はいろいろと使いどころがあると思うけど入出力をモナドで包んだのは失敗じゃないかなあ。あとstderrだけは特別扱いで(多分)グローバルにいじれるようになってるのも良い。処理の本筋と関係ない例外的なものは副作用あってもいいよね。駄目?

追記:以下は一意性型属性のついた文字列に関する話が書いてあったのだが、引用元の文面が既に更新されているので引用がおかしくなっている。詳しくはhttp://d.hatena.ne.jp/lethevert/20060411/p3http://d.hatena.ne.jp/lethevert/20060411/p5のあたりを参照。

でも、一点気になったところもあった。

Error : no content for ファイルから読み込んだ文字列は、一意性型属性が - 文字列に変換します。

これは、仕様だとすると格好悪い。文字列から一意性型属性を外す直接的な仕組みはないのかな。属性を外すためだけに内包表記とか使うのは変だ。あれ、でもそもそも一意性型属性がついてる文字列への処理の制約ってどんなんだろ。そんなキツイ制約なのかな。

さらに追記

http://d.hatena.ne.jp/lethevert/20060411/p4 にて、デバッグ用途なら、haskellにもDebug.Traceモジュールにそのまんまtraceという関数があることを教えてもらった。IOモナドを介さずに文字列と何かの値を出力できるようだ。便利。でも…なにか納得いかない…

こんなぼやきをちゃんと解説してくれたlethevertさんに感謝。

*1:あっと何も確認せずに書いたけど、そういうところを最適化するコンパイラがあったりするのかな。でもモナドの意図からすればこうなるはずだよな