考虑下面的代码(取自这里和修改以使用字节,而不是字符行)。
import java.io.{ File, InputStream, BufferedInputStream, FileInputStream }
import scalaz._, Scalaz._, effect._, iteratee.{ Iteratee => I, _ }
import std.list._
object IterateeIOExample {
type ErrorOr[+A] = EitherT[IO, Throwable, A]
def openStream(f: File) = IO(new BufferedInputStream(new FileInputStream(f)))
def readByte(s: InputStream) = IO(Some(s.read()).filter(_ != -1))
def closeStream(s: InputStream) = IO(s.close())
def tryIO[A, B](action: IO[B]) = I.iterateeT[A, ErrorOr, B] {
EitherT(action.catchLeft).map(r => I.sdone(r, I.emptyInput))
}
def enumBuffered(r: => BufferedInputStream) = new EnumeratorT[Int, ErrorOr] {
lazy val reader = r
def apply[A] = (s: StepT[Int, ErrorOr, A]) => s.mapCont(k =>
tryIO(readByte(reader)) flatMap {
case None => s.pointI
case Some(byte) => k(I.elInput(byte)) >>== apply[A]
})
}
def enumFile(f: File) = new EnumeratorT[Int, ErrorOr] {
def apply[A] = (s: StepT[Int, ErrorOr, A]) =>
tryIO(openStream(f)).flatMap(stream => I.iterateeT[Int, ErrorOr, A](
EitherT(
enumBuffered(stream).apply(s).value.run.ensuring(closeStream(stream)))))
}
def main(args: Array[String]) {
val action = (
I.consume[Int, ErrorOr, List] &=
enumFile(new File(args(0)))).run.run
println(action.unsafePerformIO())
}
}
运行在一个体面的文件(8KB)此代码产生一个StackOverflowException。 一些搜索打开了该异常可以通过使用蹦床单子,而不是IO是可以避免的,但是这似乎并不像一个很好的解决方案 - 牺牲功能纯度让程序在所有完成。 解决这个问题最显而易见的方法是使用IO或蹦床作为一个单子转换包住对方,但我无法找到的任一方的变压器版本的实现,我没有足够的功能,编程高手来知道怎么写我自己的(了解更多关于FP是这个项目的目的之一,但我怀疑创造新的单子变压器是有点以上我目前的水平)。 我想我可能只是环绕创建,运行和返回我iteratees结果的大IO动作,但感觉更多的是解决办法比的解决方案。
想必一些单子不能转换到单子变压器,所以我想知道是否有可能与大文件的工作而不会丢失IO或溢出堆栈,如果是这样,怎么样?
奖金的问题:我想不出任何方式的iteratee信号,它的时出错处理,除了有它在回报,这使得它不太容易撰写他们。 上面的代码演示了如何使用EitherT来处理枚举的错误,而是如何做的iteratees工作?