2

Ich lerne über die freien Monaden, und ich habe ein einfaches Beispiel in Scala zusammengestellt, in dem ich zwei domänenspezifische Sprachen definiere.Kostenlose Monaden stapeln

Die erste Monade behandelt die Nebenwirkungen eines Repositories. Ich habe einen Interpreter implementiert, der die staatliche Monade verwendet, um den Staat zu verwalten, aber in einem echten Programm würde ich eine Datenbank verwenden.

Die zweite Monade beschäftigt sich mit 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) 
    } 
} 

Dann kann ich diese beiden "Programme" zusammen

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

Und sie laufen zu tun

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

Ich verstehe das alles andere als ideal ist, und ich möchte Schreiben Sie ein Programm als und einen Interpreter, der unterstützt:

So sind die Fragen:

  1. Wie kann ich die zwei Monaden zusammen in eine „fullProgram“ stapeln
  2. Wie kann ich die zwei Dolmetscher in einen neuen Interpreter komponieren?
  3. Wie kann ich mit der Option befassen [Todo] von find zurückgekehrt, und dann zu geben represent
+0

dort sind einige Projekte zu generieren versuchen, diese Dinge ohne Schreibplatte. zum Beispiel FreeDSL -> https://github.com/ISCPIF/freedsl – jopasserat

Antwort

1

Antwort auf die Fragen 1 & 2:

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)