scala scalaz monad-transformers state-monad either

Estado de capas con O bien en scalaz



monad-transformers state-monad (1)

En Integrating State with Either (diapositiva 88), dado el patrón de State acodado bajo Either , ¿existe un enfoque recomendado para agregar otro tipo de estado, por ejemplo, el registro a través de algo como Writer ? Parece que el nuevo estado tiene que vivir entre el State existente y Either para aprovechar el comportamiento de fallo rápido de flatMap in flatMap .

A continuación se muestra un ejemplo ejecutable del código de la presentación, ajustado para trabajar en 2.11.8 con Scalaz 7.2.8. ¿Existe un enfoque que pueda agregar limpiamente el nuevo transformador de mónada al comportamiento existente, simplificando la refactorización? El apilamiento de StateT en Scalaz se aplica al apilamiento, pero no se ocupa del problema de ordenamiento creado por el comportamiento de flatMap error rápido.

// 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()) }


Para responder a su ejemplo específico sobre el registro, podría hacer algo como esto:

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()) } }