2015-06-04 83 views
1

我使用scalaz堆棧溢出」 Monad.whileM_來實現功能的方式while循環,如下所示:蹦牀scalaz「Monad.whileM_防止

object Main { 

    import scalaz._ 
    import Scalaz._ 

    import scala.language.higherKinds 

    case class IState(s: Int) 

    type IStateT[A] = StateT[Id, IState, A] 
    type MTransT[S[_], A] = EitherT[S, String, A] 
    type MTrans[A] = MTransT[IStateT, A] 

    def eval(k: Int): MTrans[Int] = { 
    for { 
     state <- get[IState].liftM[MTransT] 
     _ <- put(state.copy(s = (state.s + 1) % k)).liftM[MTransT] 
    } yield (k + 1) 
    } 

    def evalCond(): MTrans[Boolean] = { 
    for { 
     state <- get[IState].liftM[MTransT] 
    } yield (state.s != 0) 
    } 


    def run() = { 
    val k = 10 
    eval(k).whileM_(evalCond()).run(IState(1)) 
    } 
} 

儘管這適用於小k,這導致對於大型k(例如1000000),StackOverflow錯誤。有沒有辦法蹦牀whileM_還是有被堆安全更好的辦法?

回答

2

使用scalaz.Free.Trampoline代替scalaz.Id.Id

type IStateT[A] = StateT[Trampoline, IState, A] 

這裏使用的狀態操作返回State[S, A]這僅僅是StateT[Id, S, A]的別名。您需要使用上StateT定義解除StateT[Id, S, A]StateT[Trampoline, S, A]lift[M[_]]功能。

def eval(k: Int): MTrans[Int] = { 
    for { 
    state <- get[IState].lift[Trampoline].liftM[MTransT] 
    _ <- put(state.copy(s = (state.s + 1) % k)).lift[Trampoline].liftM[MTransT] 
    } yield (k + 1) 
} 

def evalCond(): MTrans[Boolean] = { 
    for { 
    state <- get[IState].lift[Trampoline].liftM[MTransT] 
    } yield (state.s != 0) 
} 

最後,調用.run(IState(1))現在導致Trampoline[(IState, String \/ Unit)]。您還必須另外購買run

eval(k).whileM_(evalCond()).run(IState(1)).run 
+0

monad堆棧在我的情況下給出。有沒有辦法在本地提升事物到蹦牀而不必修改monad堆棧? –

+0

我不確定這是否可行。您可能會有更好的運氣將您的問題發送到郵件列表https://groups.google.com/forum/#!forum/scalaz – drstevens