Раньше я думал, что часть цели реализации состояла в том, чтобы избежать этой самой проблемы, так что, может быть, я делаю что-то явно глупое?
Вот код:
// 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)
Примечание. Я понимаю, что это глупо :) На практике в моем классе команд есть много команд, и у меня есть команда, которая выполняет тот же цикл... в основном, опрашивая некоторое состояние, если истинно, прервите, если ложно, продолжайте ждать.
Я хочу избежать переполнения стека, которое это вызывает ... Я ДУМАЛ, что это уже было трамплин, но я думаю, мне нужно сделать это вручную снова? Есть ли чистый способ сделать это в рамках мышления свободной монады?
Обновлять:
Размышляя об этом дальше, я думаю, что проблема не в Sleep Free Monad, а скорее в монаде 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])
Но проблема в том, что он будет оценивать только один шаг. В идеале я хотел бы, чтобы runFC блокировался до тех пор, пока не будет выполнено какое-то условие (или, в этом случае, зацикливаться до тех пор, пока я не убью его, но без переполнения стека)