2016-12-29 69 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

我稍微修改了你的代码来创建一个独立运行的例子。我还为您的问题添加了一个可能的答案,如何使用Scalaz 7.2在Rúnar Bjarnason's ideas之后执行您的程序。 (我没有在Scalaz中找到自然转换的or运算符,所以我在这里添加了它。)

我还添加了一些存根,以便为您的操作提供一些操作并简化您的服务我必须为两种语言合并创建一项新服务)。此外,我将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 
相关问题