2011年9月6日火曜日

ScalazでIteratee その2

前の続き。

実際にファイルを読み込めるようにしました。

type EnumeratorM[E, M[_], A] = IterV[E, A] => M[IterV[E, A]]
def enumReader[A](r: BufferedReader): EnumeratorM[String, IO, A] = { iter =>
def loop: EnumeratorM[String, IO, A] = {
case i @ Done(_, _) => (i: IterV[String, A]).pure[IO]
case i @ Cont(k) => for {
o <- rReadLn(r)
i <- o match {
case None => i.pure[IO]
case Some(s) => s.pure[IO] >>= loop.compose(k.compose(El(_)))
}
} yield i
}
loop(iter)
}
def enumFile[A](f: File): EnumeratorM[String, IO, A] = i => bufferFile(f).bracket(closeReader)(enumReader[A](_)(i))
view raw f.scala hosted with ❤ by GitHub

EnumeratorMはIterVをモナドで包むための型です。

enumReaderはブログの例とは違いますが、一字ずつ読み込むのではなく、BufferReaderを用いて一行づつ読み込むようにしています。
<<<はScalazで定義されており、composeと同じです。
enumと同様に、Doneになるまで処理を繰り返します。

enumFileはファイルをenumReaderに渡す。
bracketにより、closeReaderが確実に行われることが保証されています。

実行例
val file = new File("src/main/resources/test.txt")
file.exists assert_=== true
def g[A](i: IterV[String, A]) = run(enumFile(file)(i).unsafePerformIO)
g(length) assert_=== Some(1)
g(peek[String] >|> head[String]) assert_=== Some(Some("hello"))
view raw g.scala hosted with ❤ by GitHub
hello
view raw test.txt hosted with ❤ by GitHub

現在、scalaz.effectsにあるIterateeな入出力関数はこれらと、標準入力からの読み込みだけです。

2 件のコメント:

  1. そういえば okomok さんが絶賛開発中(?) の

    「 Scala での Haskell DSL (?) 」

    の ken っていうライブラリの中にも Iteratee ってclass あるけれど

    https://github.com/okomok/ken/blob/7f8036d1d5c3469b2f5ff8759326b6ee21cdd3bd/src/main/scala/com/github/okomok/ken/enumerator/_Types.scala#L43-65

    返信削除
  2. >kenji
    あ、それはkenの耐久性を調べるためのもんなので
    相手にしない方がいいです 笑
    Haskellのコードのコピペですし・・・

    返信削除