使用scalaz的免费monad时如何避免堆栈溢出?

A Q*_*ker 8 scala scalaz free-monad

我以前认为实现的部分目标是避免这个问题,所以也许我做的事情显然是愚蠢的?

这是一些代码:

    // Stack overflow
import scalaz._

sealed trait Command[T]
case class Wait(ms: Long) extends Command[Unit]

case object Evaluator extends (Command ~> Id.Id) {
  override def apply[T](cmd: Command[T]) = cmd match {
    case Wait(t)  => Thread.sleep(t)
  }
}

object Api {
  def sleep(ms: Long): Free.FreeC[Command, Unit] = Free.liftFC(Wait(ms))
}

val sleep: Free.FreeC[Command, Unit] =
  Api.sleep(1).flatMap { _ => sleep }

Free.runFC(sleep)(Evaluator)
Run Code Online (Sandbox Code Playgroud)

注意:我意识到这很愚蠢:)在实践中,我的命令类有很多命令,我有一个命令,它执行相同的循环...基本上,轮询一些状态,如果真正中止,如果为假,继续等待.

我想避免这导致的堆栈溢出...我想这已经蹦了过来,但我想我需要再次手动执行它?是否有一种干净的方式在免费的monad思维方式中做到这一点?

更新:

进一步思考这个问题,我认为问题不是睡眠免费Monad,而是我们在评估中绑定的Id.Id monad ...所以我尝试了类似的东西:

case object Evaluator2 extends (Command ~> ({ type t[x] = Free[Id.Id, x] })#t) {
  override def apply[T](cmd: Command[T]) = cmd match {
    case Wait(t)  => Thread.sleep(t); Free.liftF[Id.Id, Unit](())
  }
}

Free.runFC[Command, ({ type t[x] = Free[Id.Id, x] })#t, Unit](sleep)(Evaluator2)(Free.freeMonad[Id.Id])
Run Code Online (Sandbox Code Playgroud)

但问题是它只会评估一步.理想情况下,我希望runFC阻塞,直到满足某些条件(或者在这种情况下,永远循环直到我杀死它,但没有堆栈溢出)

Apo*_*isp 6

Id单子不trampolined.您在之间的无限相互递归结束bind的方法Id单子和foldMap对自由的单子方法.使用TrampolineTask代替Id.