2017-01-08 24 views
20

W Integrating State with Either (slajd 88), z uwzględnieniem wzoru State ułożonego pod numerem Either, czy istnieje zalecane podejście do dodawania innego rodzaju stanu, na przykład rejestrowanie przez coś podobnego do Writer? Wygląda na to, że nowy stan musi żyć między istniejącymi State i Either, aby skorzystać z szybkiego działania polegającego na niepowodzeniu: Either w flatMap.Stan warstwowania przy użyciu skalowania

Poniżej znajduje się działający przykład kodu z prezentacji, przystosowany do pracy w wersji 2.11.8 z Scalaz 7.2.8. Czy istnieje podejście, które może czysto dodać nowy transformator monady do istniejącego zachowania, upraszczając refaktoryzację? Stacking StateT in Scalaz dotyczy układania w stosy, ale nie zajmuje się problemem związanym z zamówieniem utworzonym przez niepowodzenie flatMap zachowanie się 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()) 
} 

Odpowiedz

1

Aby odpowiedzieć na to konkretny przykład o zalogowaniu można zrobić coś takiego:

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

Czy rozwiązanie uogólnić do innych państw, na przykład taki, który dostarcza historia/cofnąć, etc? – Sim

+0

Obawiam się, że nie rozumiem (moje złe). Czy pytasz, czy możliwe jest cofnięcie czegoś po wykonaniu, jestem tradycyjnym sensem? –

+0

Po prostu mówię o arbitralnym nakładaniu warstw "przechwytujących", które mogą wpływać na stan przy zachowaniu tego samego typu zwrotu. Przechwytywacz "historii", w którym można wydać 'powrót (3)' jest tylko jednym przykładem. – Sim

Powiązane problemy