Слои состояния с помощью Either в скалязе

В Интеграция состояния с Either (слайд 88), учитывая шаблон State, расположенный под Either , есть ли рекомендуемый подход для добавления другого типа состояния, например, ведения журнала через что-то вроде Writer? Кажется, что новое состояние должно находиться между существующими State и Either, чтобы воспользоваться отказоустойчивым поведением Either в flatMap.

Ниже приведен работоспособный пример кода из презентации, настроенного для работы в версии 2.11.8 со Scalaz 7.2.8. Есть ли подход, который может чисто добавить новый преобразователь монад поверх существующего поведения, упростив рефакторинг? Stacking-statet-in-scalaz"> Stacking-statet-in-scalaz"> Stacking-statet-in-scalaz"> Stacking-statet-in-scalaz"> Stacking-statet-in-scalaz"> Stacking StateT в Scalaz применяется к стекированию, но не решает проблему упорядочивания, созданную ошибкой -быстрое flatMap поведение Either.

// Based on slide 88+ in https://speakerdeck.com/mpilquist/scalaz-state-monad
// Adjusted for Scala 2.11 (invariant A), Scalaz 7.2 (Pointed->Applicative) and Throwable on lhs of Either
object IntegratingStateAndEither {
  import scalaz._
  import scalaz.Scalaz._
  import EitherT._
  import scalaz.StateT.stateMonad

  type QueryStateS[A] = State[QueryState, A]
  type ET[F[_], A] = EitherT[F, Throwable, A]
  type QueryStateES[A] = ET[QueryStateS, A]

  object QueryStateES {
    def apply[A](s: QueryStateS[Throwable \/ A]): QueryStateES[A] = EitherT(s)
    def liftE[A](e: Throwable \/ A): QueryStateES[A] = apply(Applicative[QueryStateS].point(e))
    def liftS[A](s: QueryStateS[A]): QueryStateES[A] = MonadTrans[ET].liftM(s)
  }

  def runQuery(s: String, m: Model): QueryStateES[QueryResult] = for {
    query <- parseQuery(s)
    res <- performQuery(query, m)
  } yield res

  def parseQuery(s: String): QueryStateES[StatsQuery] =
    QueryStateES.liftE(new Exception("TODO parse").left)

  def performQuery(q: StatsQuery, m: Model): QueryStateES[QueryResult] =
    QueryStateES.liftE(new Exception("TODO perform").left)

  // Just examples that do nothing
  case class Model()
  case class StatsQuery()
  case class QueryResult()
  case class QueryState()

  def test = runQuery("a + b", Model()).run.run(QueryState())
}

person Sim    schedule 08.01.2017    source источник


Ответы (1)


Чтобы ответить на ваш конкретный пример регистрации, вы можете сделать что-то вроде этого:

object LayeringReaderWriterStateWithEither {
  // Based on slide 88+ in https://speakerdeck.com/mpilquist/scalaz-state-monad
  // Adjusted for Scala 2.11 (invariant A), Scalaz 7.2 (Pointed->Applicative) and Throwable on lhs of Either
  object IntegratingStateAndEither {
    import scalaz._
    import scalaz.Scalaz._
    import EitherT._

    type QueryStateS[A] = ReaderWriterState[List[String], String, QueryState, A]
    type ET[F[_], A] = EitherT[F, Throwable, A]
    type QueryStateES[A] = ET[QueryStateS, A]

    object QueryStateES {
      def apply[A](s: QueryStateS[Throwable \/ A]): QueryStateES[A] = EitherT(s)
      def liftE[A](e: Throwable \/ A): QueryStateES[A] = apply(Applicative[QueryStateS].point(e))
      def liftS[A](s: QueryStateS[A]): QueryStateES[A] = MonadTrans[ET].liftM(s)
      def log(msg: String): QueryStateES[Unit] = liftS {
        ReaderWriterState[List[String], String, QueryState, Unit] {
          case (r, s) => (msg.format(r, s), (), s).point[Id]
        }
      }
    }

    def runQuery(s: String, m: Model): QueryStateES[QueryResult] = for {
      _ ← log("Starting")
      query <- parseQuery(s)
      _ ← log(s"Got a query: $query")
      res <- performQuery(query, m)
    } yield res

    def log(msg: String): QueryStateES[Unit] =
      QueryStateES.log(msg)

    def parseQuery(s: String): QueryStateES[StatsQuery] =
      QueryStateES.liftE(new Exception(s"TODO parse $s").left)

    def performQuery(q: StatsQuery, m: Model): QueryStateES[QueryResult] =
      QueryStateES.liftE(new Exception(s"TODO perform $q in $m").left)

    // Just examples that do nothing
    case class Model()
    case class StatsQuery()
    case class QueryResult()
    case class QueryState()

    def test = runQuery("a + b", Model()).run.run(Nil, QueryState())
  }
}
person Channing Walton    schedule 17.06.2017
comment
Распространяется ли решение на другие состояния, например, с историей / отменой и т. Д.? - person Sim; 19.06.2017
comment
Боюсь, я не понимаю (мое плохое). Вы спрашиваете, можно ли что-то отменить после того, как вы сделали, я - традиционное чувство отмены? - person Channing Walton; 19.06.2017
comment
Я просто говорю о произвольном наслоении перехватчиков, которые могут повлиять на состояние, сохраняя при этом возвращаемый тип. Перехватчик истории, в котором вы могли бы выдать back(3), - лишь один из примеров. - person Sim; 19.06.2017