2

这个问题与另一个问题有关,但简化为一个更简单的情况:

我假设以下进口:

import scalaz._, Scalaz._
import Free._, effect._

我有以下生成器:

val fromOneIO: () => IO[Int] = {
  var i = 0; () => { i += 1; IO(i) }
} 
val fromOne: () => Int = {
  var i = 0; () => { i += 1; i }
}

以及以下非尾递归定义:

def rec(i: Int): Int = {
  if (i == 0) {
    fromOne()
  } else {
    rec(i - 1) + fromOne()
  }
}
def rec1(i: Int): Trampoline[Int] = {
  if (i == 0) {
    Return(fromOne())
  } else {
    suspend {
      for {
        a <- rec1(i - 1)
        b <- Return(fromOne()): Trampoline[Int]
      } yield a + b
    }
  }
}
def recio(i: Int): Trampoline[IO[Int]] = {
  if (i == 0) {
    Return(fromOneIO())
  } else {
    suspend {
      for {
        ioa <- recio(i - 1)
        iob <- Return(fromOneIO()): Trampoline[IO[Int]]
      } yield {
        for (a <- ioa; b <- iob) yield a + b
      }
    }
  }
}

结果是:

rec(100) // overflows for arg 10000
rec1(10000).run // works
recio(10000).run.unsafePerformIO() //overflows

如何设法使 IO map/flatMap 也被蹦床?看来我在第二个内部创建了其他嵌套堆栈以供理解。我是否需要编写一个TrampolineT将使用unsafePerformIO提取的 io 值并将其重新包装为挂起的代码?

4

1 回答 1

3

所以IO(_)已经被蹦床了,值得。除了 folone 的建议之外,我还可以通过更改第二个来避免溢出,如下所示:

val s = for (a <- ioa; b <- iob) yield a + b
val s1 = s.unsafePerformIO()
IO(s1)

或者像这样:

IO(for (a <- ioa; b <- iob) yield a + b).flatMap(identity)

IO(_)接受一个按名称参数,所以IO(expr)val e = expr; IO(e)不一样。这会溢出!

val s = for (a <- ioa; b <- iob) yield a + b
IO(s.unsafePerformIO())

因此,尽管这不是一个非常令人满意的答案,但似乎在可能的情况下使用应用程序或包装在另一个应用程序中IO并展平将是解决堆栈问题的解决方法。

于 2013-04-24T13:39:13.430 回答