【问题标题】:Scalaz's traverse_ with IO monadScalaz 的 traverse_ 与 IO monad
【发布时间】:2011-10-03 10:12:12
【问题描述】:

我想使用 IO monad。

但此代码不适用于大文件。 我收到了 StackOverflowError。 我尝试了-DXss 选项,但它抛出了同样的错误。

val main = for {
  l <- getFileLines(file)(collect[String, List]).map(_.run)
  _ <- l.traverse_(putStrLn)
} yield ()

我该怎么做?


我写了输出所有元素的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

这也是同样的结果。

我认为是IO实现造成的。

【问题讨论】:

  • 第一个问题是为什么/如何它不运行大文件?您是否遇到堆栈溢出错误、内存不足错误或其他错误?
  • 我收到了 StackOverflowError。我尝试了 -DXss 选项,但抛出了同样的错误。
  • 同意,我认为 IO monad 增加了一点挑战。

标签: scala scalaz


【解决方案1】:

这是因为 scalac 没有针对尾调用优化 loop 内部的 getReaderLines。 loop 是尾递归的,但我认为 case 匿名函数语法会妨碍。

编辑:实际上它甚至不是尾递归(IO monad 中的包装)在递归调用之后导致至少再调用一次。当我昨天进行测试时,我使用了类似的代码,但我已经放弃了 IO monad,然后可以使 Iteratee 尾递归。下面的文字,假设没有 IO monad...

我昨天在尝试迭代器时碰巧发现了这一点。我认为将loop 的签名更改为此会有所帮助(因此暂时您可能需要重新实现getFilesLines 和getReaderLines:

@annotations.tailrec
def loop(it: IterV[String, A]): IO[IterV[String, A]] = it match {
  // ...
}

我们可能应该将此情况报告给 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 = {
                           ^
*/

【讨论】:

  • 请报告,以便改进!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多