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:
- Wie kann ich die zwei Monaden zusammen in eine „fullProgram“ stapeln
- Wie kann ich die zwei Dolmetscher in einen neuen Interpreter komponieren?
- Wie kann ich mit der Option befassen [Todo] von
find
zurückgekehrt, und dann zu gebenrepresent
dort sind einige Projekte zu generieren versuchen, diese Dinge ohne Schreibplatte. zum Beispiel FreeDSL -> https://github.com/ISCPIF/freedsl – jopasserat