我如何使用 Scalaz 链接操作并一起解释它们?
How do I chain action and interpret them together with Scalaz?
我正在尝试学习如何使用 FreeMonads 为我的服务实现解释器。
假设我有
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]
和动作的 Monad
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
或者据说我可以使用 Shapeless 来做到这一点
它们都太复杂了。我想要的只是,在我定义了我的逻辑之后,我该如何执行它?
希望我在这里提供了足够的详细信息以获得答案。我真的很想学这个。谢谢
我稍微修改了您的代码以创建一个独立的 运行 示例。我还为您的问题添加了一个可能的答案,如何执行您的程序,遵循 Rúnar Bjarnason's ideas,使用 Scalaz 7.2。 (我没有在 Scalaz 中找到用于自然转换的 or
运算符,所以我在这里添加它。)
我还添加了一些存根来为您的操作提供一些东西 fiddle 并简化您对内部处理程序的服务(因为我必须为两种语言组合创建一个新服务)。此外,我将您的 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
我正在尝试学习如何使用 FreeMonads 为我的服务实现解释器。
假设我有
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]
和动作的 Monad
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
或者据说我可以使用 Shapeless 来做到这一点
它们都太复杂了。我想要的只是,在我定义了我的逻辑之后,我该如何执行它?
希望我在这里提供了足够的详细信息以获得答案。我真的很想学这个。谢谢
我稍微修改了您的代码以创建一个独立的 运行 示例。我还为您的问题添加了一个可能的答案,如何执行您的程序,遵循 Rúnar Bjarnason's ideas,使用 Scalaz 7.2。 (我没有在 Scalaz 中找到用于自然转换的 or
运算符,所以我在这里添加它。)
我还添加了一些存根来为您的操作提供一些东西 fiddle 并简化您对内部处理程序的服务(因为我必须为两种语言组合创建一个新服务)。此外,我将您的 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