scalaz蹦床和IO
这个问题与另一个问题相关,但简化为更简单的情况: 我假设以下情况:scalaz蹦床和IO,scala,scalaz,scalaz7,Scala,Scalaz,Scalaz7,这个问题与另一个问题相关,但简化为更简单的情况: 我假设以下情况: 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 } } 以及以下非尾部递
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
}
}
}
}
如何使IO地图/平面地图也能蹦床?我似乎在第二个堆栈中创建了其他嵌套堆栈以供理解。我是否需要编写一个蹦床
,它将使用unsafePerformIO
,并将提取的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[a:Monoid]
的一个Monoid
实例似乎起到了作用。也就是说,只需将yield
语句更改为ioa++iob
。不过,我没有什么可以推荐的。实际上,问题似乎出在IO
的Monad
实例中。如果您使用它的Applicative
实例,它似乎工作得很好yield(ioa |@| iob){uu+}
您想要的是IO
本身被践踏,而不是构建一个庞大的嵌套IO
操作链的函数。您的代码允许您构建一个IO
操作,而不会使堆栈溢出,但是,当您运行IO
操作时,构建它的方式将导致堆栈溢出。@folone,有趣的是applicative不会溢出。我试图找到它,但找不到F.lift2
的实现。我希望scalaz7仍然有从Harrah的x光片构建的交叉引用源代码。@MyseriousDan,我仍然需要处理recio
不是尾部递归的事实。所以我需要两层蹦床。所以我的问题是如何实现IO的第二个目标。
IO(for (a <- ioa; b <- iob) yield a + b).flatMap(identity)
val s = for (a <- ioa; b <- iob) yield a + b
IO(s.unsafePerformIO())