8

私は以前、実装の目標の一部はまさにこの問題を回避することだと思っていたので、明らかにばかげたことをしているのかもしれません。

ここにいくつかのコードがあります:

    // 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)

注:これはばかげていることに気づきました:)実際には、私のコマンドクラスには多くのコマンドがあり、これと同じループを実行するコマンドがあります...基本的に、いくつかの状態をポーリングし、trueの場合は中止し、falseの場合は待機します。

これが引き起こすスタック オーバーフローを回避したい...これはすでにトランポリンされていると思っていましたが、手動でやり直す必要があると思いますか? 自由なモナドの考え方の中でそれを行うためのきれいな方法はありますか?

アップデート:

これについてさらに考えてみると、問題はスリープ Free モナドではなく、評価時にバインドする Id.Id モナドにあると思います。

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])

しかし、これの問題は、1 つのステップしか評価しないことです。理想的には、何らかの条件が満たされるまで runFC をブロックしたいと思います (または、この場合、それを強制終了するまで永遠にループしますが、スタック オーバーフローは発生しません)。

4

2 に答える 2

2

@Apocalispの回答以来、スタックセーフな評価に直接(または他の「末尾再帰」モナド)に使用できるBindRec型クラスとメソッドが追加されました。詳細については、Stack Safety for Freeをお読みください。foldMapRecId

于 2016-12-13T20:10:07.570 に答える