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