Concurrent Clean : 遅延IO

以前、遅延IOを作るためにtransactionというモジュールを作ったことがあるのですが、
http://lethevert.blogspot.com/2006/10/transaction.html
これを使わなくても遅延IOは普通にできるのではないかと気づきました。

Start w
    # (_,fin,w) = fopen "input.txt" FReadText w
      (_,fout,w) = fopen "output.txt" FWriteText w
      (data,fin) = lazy_toLines fin
      fout = write_n_lines data 10 fout
      (_,w) = fclose fout w
      (_,w) = fclose fin w
    = w

という普通の書き方で、遅延IOになっているはず。
注意するところは、次の2つ。

  • lazy_toLinesの返値を(![String],!*File)とせずに、(![String],*File)としておくこと
  • foutの方を先にcloseすること

ただ、これだと、最初の10行は遅延IOで読み込んでも、結局finを閉じる前に全ての行を取り込んでしまうのでよくない。
そこで、CloseHandleというものを用意して、

    # (_,fin,cfin,w) = fopen "input.txt" FReadText w
      ...
      (_,w) = fclose cfin fin w

というように、CloseHandleを添えてcloseするようにすることで、finを遅延評価しているまま閉じるような仕組みにできるのではないかと思う。

      • -

上のCloseHandleの件は、ちょっとまずい。というのは、必要な評価が完了する前にファイルを閉じてしまうと、予期しない結果になってしまうからだ。
遅延評価なので、ファイルを閉じた後に、ファイルからデータを読み込む処理が未評価の式が残っていて、ファイルを閉じた後にデータを読み込む処理をしようとする可能性があるからだ。
ただ、それだからといって、必ずファイルハンドルを評価してからファイルを閉じなければいけないとすると(それは確かに安全だけれども)、遅延IOという便利な機能をあきらめるということになってしまう。
と、ここで思うのは、そもそもファイルを閉じる前に評価を確定するために評価しなければいけない式は、ファイルハンドルに限る必要はないということだ。なんでもいいのだ。

close :: !*File !*World -> *World
close_lazy  :: !(*CloseHandle File) *File !*World -> *World
close_lazy2 :: !.a !(*CloseHandle File) *File !*World -> (!.a, !*World)

という3種類のfclose関数を必要に応じて使い分けてあげればよいのではないか?

      • -

(TODO:検証すること)