Как остановить переход состояния, когда состояние удовлетворяет некоторому условию? - PullRequest
0 голосов
/ 29 мая 2018

Я довольно новичок в скаляр / кошках и у меня вопрос о 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 Ответ

0 голосов
/ 29 мая 2018

Вы бы использовали монаду состояния, параметризованную другой монадой, представляющей завершение.

Обычно такие параметризованные монады называются монадными преобразователями .В этом конкретном случае вы бы использовали монадный преобразователь 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.")
}
...