2015-06-02 12 views
13

Ich wickle meinen Kopf um State Monad. Triviale Beispiele sind leicht zu verstehen. Ich gehe jetzt zu einem realen Fall, in dem die Domänenobjekte zusammengesetzt sind. Zum Beispiel mit den folgenden Domänenobjekten (sie nicht viel Sinn machen, nur schiere Beispiel):Scala State Monade - Kombinieren verschiedener State-Typen

case class Master(workers: Map[String, Worker]) 
case class Worker(elapsed: Long, result: Vector[String]) 
case class Message(workerId: String, work: String, elapsed: Long) 

Unter Berücksichtigung Worker als S Arten in State[S, +A] Monade ist es ganz einfach, ein paar combinators wie diese zu schreiben:

type WorkerState[+A] = State[Worker, A] 
def update(message: Message): WorkerState[Unit] = State.modify { w => 
    w.copy(elapsed = w.elapsed + message.elapsed, 
      result = w.result :+ message.work) 
} 
def getWork: WorkerState[Vector[String]] = State { w => (w.result, w) } 
def getElapsed: WorkerState[Long] = State { w => (w.elapsed, w) } 
def updateAndGetElapsed(message: Message): WorkerState[Long] = for { 
    _ <- update(message) 
    elapsed <- getElapsed 
} yield elapsed 
// etc. 

Was ist der idiomatische Weg, diese mit den Master State Kombinatoren zu kombinieren? z.B.

type MasterState[+A] = State[Master, A] 
def updateAndGetElapsedTime(message: Message): MasterState[Option[Long]] 

Ich kann dies umzusetzen etwa so:

def updateAndGetElapsedTime(message: Message): MasterState[Option[Long]] = 
    State { m => 
     m.workers.get(message.workerId) match { 
      case None => (None, m) 
      case Some(w) => 
       val (t, newW) = updateAndGetElapsed(message).run(w) 
       (Some(t), m.copy(m.workers.updated(message.workerId, newW)) 
     } 
    } 

Was Ich mag nicht, dass ich manuell den Staat Monade innerhalb des letzten Transformator laufen. Mein Beispiel aus der realen Welt ist ein bisschen mehr involviert. Mit diesem Ansatz wird es schnell unordentlich.

Gibt es mehr idiomatische Möglichkeit, diese Art von inkrementellen Updates auszuführen?

+0

Nette Frage! Beziehen Sie sich auf eine konkrete "State" Implementierung wie "Scalaz"? – Odomontois

+0

Es ist definitiv ein schönes Beispiel für 'LensT', ich kann es kaum erwarten, die Antwort eines Experten zu sehen. – Odomontois

Antwort

8

Es ist möglich, dies durch die Kombination von Objektiven und der State Monad ziemlich schön zu machen. Zuerst für das Setup (ich habe bearbeiten Sie leicht, um es mit Scalaz 7.1 zu kompilieren):

case class Master(workers: Map[String, Worker]) 
case class Worker(elapsed: Long, result: Vector[String]) 
case class Message(workerId: String, work: String, elapsed: Long) 

import scalaz._, Scalaz._ 

type WorkerState[A] = State[Worker, A] 

def update(message: Message): WorkerState[Unit] = State.modify { w => 
    w.copy(
    elapsed = w.elapsed + message.elapsed, 
    result = w.result :+ message.work 
) 
} 

def getWork: WorkerState[Vector[String]] = State.gets(_.result) 
def getElapsed: WorkerState[Long] = State.gets(_.elapsed) 
def updateAndGetElapsed(message: Message): WorkerState[Long] = for { 
    _ <- update(message) 
    elapsed <- getElapsed 
} yield elapsed 

Und jetzt ein paar allgemeinen Zwecken Linsen, die es uns ermöglichen, in einem Master aussehen:

val workersLens: Lens[Master, Map[String, Worker]] = Lens.lensu(
    (m, ws) => m.copy(workers = ws), 
    _.workers 
) 

def workerLens(workerId: String): PLens[Master, Worker] = 
    workersLens.partial andThen PLens.mapVPLens(workerId) 

Und dann sind wir im Grunde getan:

def updateAndGetElapsedTime(message: Message): State[Master, Option[Long]] = 
    workerLens(message.workerId) %%= updateAndGetElapsed(message) 

Hier die %%= uns gerade erzählt in welchem ​​Zustand Operation auszuführen, sobald wir in die entsprechenden worke gezoomt haben r über unsere Linse.

+0

Ich führe meine eigenen Monad Implementierungen. Verstehe ich es richtig, dass, um diese Arbeit zu bekommen, ich eine Implementierung von Lens und Integration des Staates mit Lens benötige (insbesondere dass %% = Operation)? –

+0

Auch was ist Ihre Meinung über die Verwendung von Scalaz vs handschriftliche Implementierungen von Monaden? –

+0

Wenn Sie so etwas tun, würde ich Scalaz oder Katzen dringend empfehlen, anstatt Ihre eigenen zu rollen. –