2016-12-29 1 views
0

Ich versuche zu lernen, wie man FreeMonads benutzt, um Dolmetscher für meine Dienste zu implementieren.Wie kette ich Aktionen und interpretiere sie zusammen mit Scalaz?

nehme ich

sealed trait ServiceAction[T] extends Product with Serializable 
case class ConsumeCommand(cmd: AccruePoints) extends ServiceAction[AccruePointModel] 
case class CreateEvent(evt: PointsAccruedEvent) extends ServiceAction[PointsAccruedEvent] 

sealed trait LogAction[T] extends Product with Serializable 
case class Info(msg: String) extends LogAction[Unit] 
case class Error(msg: String) extends LogAction[Unit] 

und eine Monade der Aktion

type LogActionF[A] = Free[LogAction, A] 
type ServiceActionF[A] = Free[ServiceAction, A] 

Als nächstes haben, definiere ich meinen Dienst so:

trait PointAccrualService { 
    def consume(cmd: AccruePoints): ServiceActionF[AccruePointModel] = Free.liftF(ConsumeCommand(cmd)) 
    def emit(evt: PointsAccruedEvent) : ServiceActionF[PointsAccruedEvent] = Free.liftF(CreateEvent(evt)) 
} 

und

trait LogService { 
    def info(msg: String) : LogActionF[Unit] = Free.liftF(Info(msg)) 
    def error(msg: String) : LogActionF[Unit] = Free.liftF(Error(msg)) 
} 

mit einem Objekt jedes

object LogService extends LogService 
object PointAccrualService extends PointAccrualService 

Mein LogServiceInterpreter ist wie folgt:

case class LogServiceConsoleInterpreter() extends LogServiceInterpreter { 
    def apply[A](action: LogActionF[A]): Task[A] = action.foldMap(handler)    

    protected def handler = new (LogAction ~> Task) { 
    override def apply[A](fa: LogAction[A]) = fa match { 
     case Info(m) => 
     now(info(m)) 
     case Error(m) => 
     now(error(m)) 
    } 
    } 

    def info(msg: String): Unit = { 
    println(s"INFO: $msg") 
    } 

    def error(msg: String): Unit = { 
    println(s"ERROR: $msg") 
    } 
} 

Und mein PointAccuralServiceInterpreter ist wie folgt:

case class PointAccuralServiceInterpreter() { 
    def apply[A] (action: ServiceActionF[A]) : Task[A] = action.foldMap(handler) 
    protected def handler = new (ServiceAction ~> Task) { 
    override def apply[A](fa: ServiceAction[A]): Task[A] = fa match { 
     case ConsumeCommand(cmd) => { 
     println("Service ConsumeCommand:" + cmd) 
     now(cmd) 
     } 
     case CreateEvent(evt) => { 
     println("Service CreateEvent:" + evt) 
     now(evt) 
     } 
    } 
    } 
} 

Meine Logik einfach ist, ich will um zu loggen und meinen Befehl zu konsumieren und dann ein Ereignis zu erstellen, ähnlich einer Ereignisquelle:

val ret = for { 
    _ <- logService.info("Command: " + cmd) 
    model <- service.consume(cmd) 
    _ <- logService.info("Model: " + model) 
    evt <- service.emit(model.toEvent("200", "Event Sent")) 
    _ <- logService.info("Event:" + evt) 
} yield evt 

Dieser Code wird nicht einmal tatsächlich kompiliert.

Was soll ich von hier aus machen? Ich denke, ich sollte Coproduct benutzen, um sie zu verketten und dieses Stück Logik auszuführen, indem ich meinen Dolmetscher füttere.

fand ich etwas hier https://groups.google.com/forum/#!topic/scalaz/sHxFsFpE86c

oder es gesagt ich Shapeless so Folding a list of different types using Shapeless in Scala

Sie sind zu tun, können alle zu kompliziert. Alles was ich will ist, nachdem ich meine Logik definiert habe, wie führe ich sie aus?

Ich hoffe, ich habe genug Details hier für eine Antwort. Ich möchte das wirklich lernen. Danke

+2

Ihre Frage ist überhaupt nicht klar. – pedrofurla

+0

Entschuldigung, lass mich mehr Code hinzufügen. Ich stürzte mit anderen Leuten zu Mittag.Ich hätte mehr in den Körper tun sollen – sowen

Antwort

1

Ich modifizierte leicht Ihren Code, um ein in sich geschlossenes laufendes Beispiel zu verursachen. Ich habe auch eine mögliche Antwort auf Ihre Frage, wie Sie Ihr Programm ausführen, gefolgt Rúnar Bjarnason's ideas, mit Scalaz 7.2. (Ich habe den Operator or für die natürlichen Umwandlungen in Scalaz nicht gefunden, also fügte ich es hier hinzu.)

Ich fügte auch einige Stubs hinzu, um Ihren Handlungen etwas zu geben, um mit Ihren und den zu behandelnden Bedienern innen zu spielen (seit Ich musste einen neuen Dienst für beide Sprachen zusammen erstellen). Außerdem habe ich Ihre Task.now{...} zu Task{...} geändert, um eine asynchrone Aufgabe zu erstellen, die in der letzten Codezeile ausgeführt wird. Hier

ist der vollständige Code:

import scala.language.{higherKinds, implicitConversions} 

import scalaz._ 
import scalaz.concurrent.Task 

/* Stubs */ 

case class AccruePoints() 
case class AccruePointModel(cmd: AccruePoints) { 
    def toEvent(code: String, description: String): PointsAccruedEvent = PointsAccruedEvent(code, description) 
} 
case class PointsAccruedEvent(code: String, description: String) 

/* Actions */ 

sealed trait ServiceAction[T] extends Product with Serializable 

case class ConsumeCommand(cmd: AccruePoints) extends ServiceAction[AccruePointModel] 
case class CreateEvent(evt: PointsAccruedEvent) extends ServiceAction[PointsAccruedEvent] 

sealed trait LogAction[T] extends Product with Serializable 

case class Info(msg: String) extends LogAction[Unit] 
case class Error(msg: String) extends LogAction[Unit] 

/* Handlers */ 

object PointAccuralServiceHandler extends (ServiceAction ~> Task) { 
    override def apply[A](fa: ServiceAction[A]): Task[A] = fa match { 
    case ConsumeCommand(cmd) => { 
     println("Service ConsumeCommand:" + cmd) 
     Task(consume(cmd)) 
    } 
    case CreateEvent(evt) => { 
     println("Service CreateEvent:" + evt) 
     Task(evt) 
    } 
    } 

    def consume(cmd: AccruePoints): AccruePointModel = 
    AccruePointModel(cmd) 
} 

case object LogServiceConsoleHandler extends (LogAction ~> Task) { 
    override def apply[A](fa: LogAction[A]): Task[A] = fa match { 
    case Info(m) => 
     Task(info(m)) 
    case Error(m) => 
     Task(error(m)) 
    } 

    def info(msg: String): Unit = { 
    println(s"INFO: $msg") 
    } 

    def error(msg: String): Unit = { 
    println(s"ERROR: $msg") 
    } 
} 

/* Execution */ 

class Service[F[_]](implicit I1: Inject[ServiceAction, F], I2: Inject[LogAction, F]) { 
    def consume(cmd: AccruePoints): Free[F, AccruePointModel] = Free.liftF(I1(ConsumeCommand(cmd))) 

    def emit(evt: PointsAccruedEvent): Free[F, PointsAccruedEvent] = Free.liftF(I1(CreateEvent(evt))) 

    def info(msg: String): Free[F, Unit] = Free.liftF(I2(Info(msg))) 

    def error(msg: String): Free[F, Unit] = Free.liftF(I2(Error(msg))) 
} 

object Service { 
    implicit def instance[F[_]](implicit I1: Inject[ServiceAction, F], I2: Inject[LogAction, F]) = new Service[F] 
} 

def prg[F[_]](implicit service: Service[F]) = { 
    val cmd = AccruePoints() 
    for { 
    _ <- service.info("Command: " + cmd) 
    model <- service.consume(cmd) 
    _ <- service.info("Model: " + model) 
    evt <- service.emit(model.toEvent("200", "Event Sent")) 
    _ <- service.info("Event:" + evt) 
    } yield evt 
} 

type App[A] = Coproduct[ServiceAction, LogAction, A] 

def or[F[_], G[_], H[_]](f: F ~> H, g: G ~> H) = 
    new (({type t[x] = Coproduct[F, G, x]})#t ~> H) { 
    override def apply[A](c: Coproduct[F, G, A]): H[A] = c.run match { 
     case -\/(fa) => f(fa) 
     case \/-(ga) => g(ga) 
    } 
    } 

val app = prg[App] 

val ret = app.foldMap(or(PointAccuralServiceHandler, LogServiceConsoleHandler)) 
ret.unsafePerformSync 
Verwandte Themen