我对scalaz / cats很新,并对State
monad(cats
或scalaz
无关紧要)提出疑问。请考虑以下使用Stack
的着名示例:
object StateTest {
type Stack = List[Int]
def main(args: Array[String]) = {
println(transition.run(List(1, 2, 3, 4)).value) //(List(4),Some(3))
println(transition.run(List(1, 2)).value) //(List(),None)
}
def transition: State[Stack, Option[Int]] = for {
_ <- pop
_ <- pop
a <- pop
} yield a
def pop: State[Stack, Option[Int]] = State {
case x::xs => (xs, Some(x))
case Nil => (Nil, None)
}
}
问题是我想执行状态转换(pop
),直到状态(List[Int]
)满足某些条件(我想检查List[Int]::isEmpty
),之后立即停止。
在当前实现中,我只能在调用run
后知道状态是否满足条件。
是否有可能在状态monad的猫/ scalaz中这样做或者我还需要别的东西?
答案 0 :(得分:2)
您将使用由另一个代表终止的monad参数化的状态monad。
通常,这种参数化monad称为 monad变换器。在这种特定情况下,您将使用StateT
monad变换器。模数一些实现细节,StateT
等同于
type StateT[F[_], S, A] = S => F[(S, A)]
现在,您可以选择F
为Option
,代表立即终止。
import scalaz.StateT
import scalaz.std.option._
object StateTest {
type Stack = List[Int]
def main(args: Array[String]) = {
println(transition.run(List(1, 2, 3, 4))) // Some((List(4), 3))
println(transition.run(List(1, 2))) // None
}
def transition: StateT[Option, Stack, Int] = for {
_ <- pop
_ <- pop
a <- pop
} yield a
def pop: StateT[Option, Stack, Int] = StateT {
case x::xs => Some((xs, x))
case Nil => None
}
}
如果您希望在提前终止的情况下返回B
类型的某些值,则可以使用Either[B, ?]
代替Option
来参数化StateT
:
type ErrorOr[A] = Either[String, A]
def pop1: StateT[ErrorOr, Stack, Int] = StateT {
case x::xs => Right((xs, x))
case Nil => Left("Cannot pop from an empty stack.")
}