使用scalaz

时间:2017-01-08 18:09:33

标签: scala scalaz monad-transformers state-monad either

Integrating State with Either(幻灯片88)中,鉴于在State下分层Either的模式,是否有建议的方法来添加其他类型的状态,例如,通过{ {1}}?新状态似乎必须存在于现有WriterState之间,才能利用EitherEither的快速失败行为。

下面是演示文稿中代码的可运行示例,调整为使用Scalaz 7.2.8在2.11.8上工作。有没有一种方法可以在现有行为的基础上干净地添加新的monad变换器,简化了重构? Stacking StateT in Scalaz适用于堆叠,但不处理flatMap的快速flatMap行为所造成的排序问题。

Either

1 个答案:

答案 0 :(得分: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())
  }
}