2016-12-29 10 views
0

Я пытаюсь узнать, как использовать FreeMonads для внедрения интерпретаторов для моих сервисов.Как связать действие и интерпретировать их вместе с Scalaz?

Пусть у меня есть

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] 

и монаду действия

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

Далее, я определяю свою службу, как это:

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

и

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

с объектом каждого

object LogService extends LogService 
object PointAccrualService extends PointAccrualService 

Мой LogServiceInterpreter как это:

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") 
    } 
} 

Точно так же, мой PointAccuralServiceInterpreter, как это:

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) 
     } 
    } 
    } 
} 

Моя логика проста, я хочу для регистрации и использования моей команды, а затем создания события, похожего на источник событий:

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 

Этот код даже не компилируется на самом деле.

Что мне делать? Я думаю, что я должен использовать Coproduct, чтобы связать их и выполнить эту логику, кормив моего переводчика.

я нашел что-то здесь https://groups.google.com/forum/#!topic/scalaz/sHxFsFpE86c

или он сказал, что я могу использовать бесформенные сделать так Folding a list of different types using Shapeless in Scala

Они все слишком сложно. Все, что я хочу, после того, как я определяю свою логику, как ее выполнить?

Надеюсь, что я приведу здесь достаточно подробностей для ответа. Я действительно хочу это узнать. Спасибо

+2

Ваш вопрос не понятен вообще. – pedrofurla

+0

Извините, позвольте мне добавить еще код. Я бросился на обед с другими людьми.Я должен был положить больше в тело – sowen

ответ

1

Я немного изменил ваш код, чтобы создать автономный пример работы. Я также добавил возможный ответ на ваш вопрос, как выполнить вашу программу, следуя Rúnar Bjarnason's ideas, используя Scalaz 7.2. (Я не нашел оператора or для естественных преобразований в Scalaz, поэтому я добавил его здесь.)

Я также добавил несколько заглушек, чтобы дать вашим действиям что-то, чтобы возиться с вами и упростить ваши сервисы для обработчиков внутри (с тех пор Мне пришлось создать новый сервис для обоих языков в сочетании). Кроме того, я изменил ваш Task.now{...} на Task{...}, чтобы создать асинхронную задачу, которая выполняется в последней строке кода.

Вот полный код:

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