In precedenza avevo pensato che parte dello scopo dell'implementazione fosse evitare questo stesso problema, quindi forse sto facendo qualcosa di ovviamente stupido?Come evitare l'overflow dello stack quando si utilizza la monade gratuita di scalaz?
Ecco il codice:
// 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)
Nota: Mi rendo conto che questo è stupido :) In pratica, la mia classe di comando ha molti comandi, e ho un comando che fa questo stesso circuito ... in fondo, sondare qualche stato, se il vero abortire, se è falso, continuare ad aspettare.
Voglio evitare l'overflow dello stack che questo causa ... PENSIERO che questo fosse già trampolino, ma suppongo di doverlo ripetere manualmente? C'è un modo pulito per farlo nel modo di pensare libero monade?
Aggiornamento:
Pensando ulteriormente su questo, credo che il problema non è il sonno libero Monade, ma piuttosto la taht Id.Id monade, leghiamo in data di valutazione ... quindi ho cercato qualcosa di simile:
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])
Ma il problema con questo è che valuterà solo un passaggio. Idealmente mi piacerebbe eseguire runFC per bloccare fino a quando alcune condizioni sono soddisfatte (o in questo caso, per loop per sempre finché non lo uccido, ma senza un overflow dello stack)