2017-01-08 6 views
20

(スライド88)で、StateのパターンがEitherの下にあると、Writerのような別のタイプの状態を追加することをお勧めしますか?フェール・ファーストの動作をEitherflatMapに利用するには、新しい状態が既存のStateEitherの間になければならないようです。スカラーズのいずれかで状態をレイヤー化

以下は、Scalaz 7.2.8で2.11.8で動作するように調整されたプレゼンテーションのコードの実行可能な例です。既存のビヘイビアの上に新しいモナドトランスを追加することができ、リファクタリングを簡素化する方法はありますか? Stacking StateT in 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()) 
} 

答えて

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

ソリューションは、他の州への一般化していが、例えば、歴史を提供1の/ etc、元に戻しますか? – Sim

+0

私は理解できません(私の悪いこと)。私は伝統的なアンドゥーセンスだと思っていますか? –

+0

私は、戻り値の型を同じに保ちながら状態に影響を与える可能性のある「インターセプタ」の任意のレイヤリングについて簡単に説明しています。 'back(3)'を発行できる "履歴"インターセプタは単なる一例です。 – Sim

関連する問題