基于免费蒙纳德的代数组合

时间:2018-03-12 12:55:17

标签: scala scala-cats free-monad

假设我有以下代数用于处理文件系统:

sealed trait Fs[A]
case class Ls(path: String) extends Fs[Seq[String]]
case class Cp(from: String, to: String) extends Fs[Unit]

def ls(path: String) = Free.liftF(Ls(path))
def cp(from: String, to: String) = Free.liftF(Cp(from, to))

以下代数解释器:

def fsInterpreter = new (Fs ~> IO) {
  def apply[A](fa: Fs[A]) = fa match {
    case Ls(path) => IO(Seq(path))
    case Cp(from, to) => IO(())
  }
}

现在假设我想构建另一个使用第一个代数的代数。 E.g:

sealed trait PathOps[A]
case class SourcePath(template: String) extends PathOps[String]

def sourcePath(template: String) = Free.liftF(SourcePath(template))

接下来我想为PathOps ~> IO写一个解释器,它会做这样的事情:

for {
  paths <- ls(template)
} yield paths.head

换句话说,我PathOps的翻译应该调用Fs代数。

我该怎么做?

1 个答案:

答案 0 :(得分:2)

我假设您想要编写两个解释器PathOps ~> Free[Fs, ?]Fs ~> IO,然后将它们组成一个解释器PathOps ~> IO

下面是一个可编辑的例子。以下是我用于此示例的所有导入:

import cats.~>
import cats.free.Free
import cats.free.Free.liftF

这是IO和你的代数的模拟实现:

// just for this example
type IO[X] = X 
object IO {
  def apply[A](a: A): IO[A] = a
}

sealed trait Fs[A]
case class Ls(path: String) extends Fs[Seq[String]]
case class Cp(from: String, to: String) extends Fs[Unit]
type FreeFs[A] = Free[Fs, A]

def ls(path: String) = Free.liftF(Ls(path))
def cp(from: String, to: String) = Free.liftF(Cp(from, to))

这是从您的代码中复制的解释器Fs ~> IO

def fsToIoInterpreter = new (Fs ~> IO) {
  def apply[A](fa: Fs[A]) = fa match {
    case Ls(path) => IO(Seq(path))
    case Cp(from, to) => IO(())
  }
}

sealed trait PathOps[A]
case class SourcePath(template: String) extends PathOps[String]

def sourcePath(template: String) = Free.liftF(SourcePath(template))

这是您的for - 理解转换为PathOps ~> Free[Fs, ?] - 解释器:

val pathToFsInterpreter = new (PathOps ~> FreeFs) {
  def apply[A](p: PathOps[A]): FreeFs[A] = p match {
    case SourcePath(template) => {
      for {
        paths <- ls(template)
      } yield paths.head
    }
  }
}

现在,您可以使用Fs ~> IOFree[Fs, ?] ~> IO提升为Free.foldMap,并使用PathOps ~> Free[Fs, ?]andThen - 解释程序进行撰写:

val pathToIo: PathOps ~> IO = 
  pathToFsInterpreter andThen 
  Free.foldMap(fsToIoInterpreter)

这为您提供了PathOps ~> IO的解释器,它由两个可以单独测试的独立层组成。

相关问题