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