Как остановить переход состояния, когда состояние удовлетворяет некоторому условию?
Я довольно новичок в скалязе / кошках и у меня есть вопрос о State
монада (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
,
Можно ли это сделать в кошках / скалазах с помощью государственной монады или мне нужно что-то еще?
1 ответ
Вы бы использовали монаду состояния, параметризованную другой монадой, представляющей завершение.
Обычно такие параметризованные монады называют монадными трансформаторами. В этом конкретном случае вы бы использовали StateT
монадный трансформатор. По модулю некоторые детали реализации, 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.")
}