Cats Free 基于 Monad 的代数组合
Cats Free Monad based algebras composition
假设我有以下处理文件系统的代数:
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(())
}
}
现在假设我想构建另一个使用第一个代数的代数。例如:
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
代数。
我该怎么做?
我假设你想写两个解释器PathOps ~> Free[Fs, ?]
和 Fs ~> IO
,然后将它们组合成一个解释器 PathOps ~> IO
.
下面是一个可编译的例子。以下是我在此示例中使用的所有导入:
import cats.~>
import cats.free.Free
import cats.free.Free.liftF
这是 IO
的 mock-implementation 和你的代数:
// 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
}
}
}
现在您可以使用 Free.foldMap
将 Fs ~> IO
提升为 Free[Fs, ?] ~> IO
,并使用 andThen
将其与 PathOps ~> Free[Fs, ?]
-解释器组合:
val pathToIo: PathOps ~> IO =
pathToFsInterpreter andThen
Free.foldMap(fsToIoInterpreter)
这为您提供了来自 PathOps ~> IO
的解释器,它由两个可以单独测试的独立层组成。
假设我有以下处理文件系统的代数:
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(())
}
}
现在假设我想构建另一个使用第一个代数的代数。例如:
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
代数。
我该怎么做?
我假设你想写两个解释器PathOps ~> Free[Fs, ?]
和 Fs ~> IO
,然后将它们组合成一个解释器 PathOps ~> IO
.
下面是一个可编译的例子。以下是我在此示例中使用的所有导入:
import cats.~>
import cats.free.Free
import cats.free.Free.liftF
这是 IO
的 mock-implementation 和你的代数:
// 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
}
}
}
现在您可以使用 Free.foldMap
将 Fs ~> IO
提升为 Free[Fs, ?] ~> IO
,并使用 andThen
将其与 PathOps ~> Free[Fs, ?]
-解释器组合:
val pathToIo: PathOps ~> IO =
pathToFsInterpreter andThen
Free.foldMap(fsToIoInterpreter)
这为您提供了来自 PathOps ~> IO
的解释器,它由两个可以单独测试的独立层组成。