我想使用IO monad.
但是此代码不能与大文件一起运行.我收到了StackOverflowError.我尝试了该-DXss选项,但它会抛出同样的错误.
val main = for {
l <- getFileLines(file)(collect[String, List]).map(_.run)
_ <- l.traverse_(putStrLn)
} yield ()
Run Code Online (Sandbox Code Playgroud)
我该怎么做?
我写了输出所有元素的Iteratee.
def putStrLn[E: Show]: IterV[E, IO[Unit]] = {
import IterV._
def step(i: IO[Unit])(input: Input[E]): IterV[E, IO[Unit]] =
input(el = e => Cont(step(i >|> effects.putStrLn(e.shows))),
empty = Cont(step(i)),
eof = Done(i, EOF[E]))
Cont(step(mzero[IO[Unit]]))
}
val main = for {
i <- getFileLines(file)(putStrLn).map(_.run)
} yield i.unsafePerformIO
Run Code Online (Sandbox Code Playgroud)
这也是同样的结果.
我认为是由IO实现引起的.
这是因为scalac没有loop在内部getReaderLines针对尾部调用进行优化。loop是尾递归,但我认为case匿名函数语法会妨碍。
编辑:实际上它甚至不是尾递归(IO monad 中的包装)会导致递归调用后至少再调用一次。当我昨天进行测试时,我使用了类似的代码,但我删除了 IO monad,然后可以使 Iteratee 尾部递归。下面的文本假设没有 IO monad...
我昨天在对 iteratee 进行实验时碰巧发现了这一点。我认为将 的签名更改loop为此会有所帮助(因此暂时您可能需要重新实现getFilesLines和getReaderLines:
@annotations.tailrec
def loop(it: IterV[String, A]): IO[IterV[String, A]] = it match {
// ...
}
Run Code Online (Sandbox Code Playgroud)
我们或许应该向 scalaz 人员报告这一点(并且可能会为 scala 开一个增强票)。
这显示了发生的情况(代码大致类似于getReaderLines.loop):
@annotation.tailrec
def f(i: Int): Int = i match {
case 0 => 0
case x => f(x - 1)
}
// f: (i: Int)Int
@annotation.tailrec
def g: Int => Int = {
case 0 => 0
case x => g(x - 1)
}
/* error: could not optimize @tailrec annotated method g:
it contains a recursive call not in tail position
def g: Int => Int = {
^
*/
Run Code Online (Sandbox Code Playgroud)