2015-04-15 17 views
8

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)

risposta

6

La monade Id non è trampolino. Si finisce in un'infinita ricorsione reciproca tra il metodo bind per la monade Id e il metodo foldMap per la monade libera. Utilizzare Trampoline o Task anziché Id.

2

Da @ risposta di Apocalisp, v'è stata aggiunta di BindRec typeclass e foldMapRec metodo che può essere utilizzato per la valutazione dello stack-safe direttamente Id (o qualsiasi altro "tail-ricorsiva" monade). Per dettagli, leggere Stack Safety for Free.

Problemi correlati