2

무료 모나드에 대해 배우고 있으며 스칼라에 두 개의 도메인 특정 언어를 정의하는 데 사용하는 간단한 예제를 작성했습니다.무료 Monads 스태킹

첫 번째 모나드는 저장소의 부작용을 처리합니다. 상태를 관리하기 위해 상태 모나드를 사용하는 통역사를 구현했지만 실제 프로그램에서는 데이터베이스를 사용합니다.

두 번째 모나드는 IO를 처리합니다.

import cats.data.State 
import cats.{Id, ~>} 
import cats.free.Free 
import cats.free.Free.liftF 

final case class Todo(title: String, body: String) 
def represent(todo: Todo) = s"${todo.title}: ${todo.body}" 


sealed trait CRUDActionA[T] 
final case class Find(key: String) extends CRUDActionA[Option[Todo]] 
final case class Add(data: Todo) extends CRUDActionA[Unit] 

type CRUDAction[T] = Free[CRUDActionA, T] 
def find(key: String): CRUDAction[Option[Todo]] = liftF[CRUDActionA, Option[Todo]](Find(key)) 
def add(data: Todo): CRUDAction[Unit] = liftF[CRUDActionA, Unit](Add(data)) 

type TodosState[A] = State[List[Todo], A] 
val repository: CRUDActionA ~> TodosState = new (CRUDActionA ~> TodosState) { 
    def apply[T](fa: CRUDActionA[T]): TodosState[T] = fa match { 
    case Add(todo) => State.modify(todos => todos :+ todo) 
    case Find(title) => State.inspect(todos => todos find (_.title == title)) 
    } 
} 


sealed trait IOActionA[T] 
final case class Out(str: String) extends IOActionA[Unit] 

type IOAction[T] = Free[IOActionA, T] 
def out(str: String): IOAction[Unit] = liftF[IOActionA, Unit](Out(str)) 

val io: IOActionA ~> Id = new (IOActionA ~> Id) { 
    override def apply[A](fa: IOActionA[A]): Id[A] = fa match { 
    case Out(todo) => println(todo) 
    } 
} 

다음, 나는이 두 가지 "프로그램"

def addNewTodo: Free[CRUDActionA, Option[Todo]] = for { 
    _ <- add(Todo(title = "Must do", body = "Must do something")) 
    todo <- find("Must do") 
} yield todo 

def outProgram(todo: Todo): IOAction[Unit] = for { 
    _ <- out(represent(todo)) 
} yield() 

을 넣어

val (_, mayBeTodo) = (addNewTodo foldMap repository run List()).value 
outProgram(mayBeTodo.get).foldMap(io) 

내가 여기까지 이상에서 이해하고이를 실행하고 나는 싶습니다 수 있습니다 프로그램을 작성하고 통역사는 다음을 지원합니다.

def fullProgram = for { 
    _ <- add(Todo(title = "Must do", body = "Must do something")) 
    todo <- find("Must do")  // This is an option!!! 
    _ <- out(represent(todo)) // But represent expects a Todo 
} yield() 
(210)

그래서 질문은 다음과 같습니다

  • 가 어떻게 새로운 해석으로이 통역을 구성 할 수
    1. 은 어떻게 "fullProgram"에 함께 두 개의 모나드를 쌓을 수 있나요? 그때 find에 의해 반환되는 옵션 [도도] 처리하고, 어떻게
    2. 1 &이 질문에 represent
    +0

    몇 가지 프로젝트를 생성하기 위해 노력하고 거기 이러한 것들은 상용구를 쓰지 않고도 가능합니다. 예 : FreeDSL -> https://github.com/ISCPIF/freedsl – jopasserat

    답변

    1

    대답에 전달 :

    type TodoApp[A] = Coproduct[IOActionA, CRUDActionA, A] 
    
    
        class CRUDActions[F[_]](implicit I: Inject[CRUDActionA, F]) { 
        def find(key: String): Free[F, Option[Todo]] = Free.inject[CRUDActionA, F](Find(key)) 
        def add(data: Todo): Free[F, Unit] = Free.inject[CRUDActionA, F](Add(data)) 
        } 
    
        object CRUDActions { 
        implicit def crudActions[F[_]](implicit I: Inject[CRUDActionA, F]): CRUDActions[F] = new CRUDActions[F] 
        } 
    
        class IOActions[F[_]](implicit I: Inject[IOActionA, F]) { 
        def out(str: String): Free[F, Unit] = Free.inject[IOActionA, F](Out(str)) 
        } 
    
        object IOActions { 
        implicit def ioActions[F[_]](implicit I: Inject[IOActionA, F]): IOActions[F] = new IOActions[F] 
        } 
    
        def fullProgram(implicit C : CRUDActions[TodoApp], I : IOActions[TodoApp]): Free[TodoApp, Unit] = { 
        for { 
         _ <- C.add(Todo(title = "Must do", body = "Must do something")) 
         todo <- C.find("Must do") 
         _ <- I.out(represent(todo.get)) 
        } yield() 
        } 
    
        object ConsoleCatsInterpreter extends (IOActionA ~> Id) { 
        def apply[A](i: IOActionA[A]) = i match { 
         case Out(prompt) => println(prompt).asInstanceOf[A] 
        } 
        } 
    
        object MutableListCrudInterpreter extends (CRUDActionA ~> Id) { 
        val data = new ListBuffer[Todo] 
    
        override def apply[A](fa: CRUDActionA[A]): Id[A] = fa match { 
         case Add(todo) => data.append(todo).asInstanceOf[A] 
         case Find(title) => data.find(_.title == title).asInstanceOf[A] 
        } 
        } 
    
        val interpreter: TodoApp ~> Id = ConsoleCatsInterpreter or MutableListCrudInterpreter 
    
        fullProgram.foldMap(interpreter)