这个问题与其他question有关,但简化为更简单的情况:
我假设以下导入:
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
的{{1}}并将提取的io值重新包装到暂停中?
答案 0 :(得分:3)
所以IO(_)
已经trampolined,因为它的价值。除了来自folone的建议之外,我还可以通过更改第二个 for 来避免溢出:
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
中包装并且展平将是吹制堆栈的解决方法。