我正在使用scalaz' Monad.whileM_
以函数方式实现while循环,如下所示:
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_
还是有更好的方法来安全堆叠?
答案 0 :(得分:2)
使用scalaz.Free.Trampoline
代替scalaz.Id.Id
。
type IStateT[A] = StateT[Trampoline, IState, A]
此处使用的状态操作返回State[S, A]
,它只是StateT[Id, S, A]
的别名。您需要使用lift[M[_]]
上定义的StateT
函数将StateT[Id, S, A]
提升为StateT[Trampoline, S, A]
。
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