如何为猫中使用的 ADT 创建函子 Free Monad
How to create a Functor for an ADT used in a cats Free Monad
我正在 cats.free.Free Monad 库的帮助下使用 case classes 编写 DSL。 DSL 将由接收消息的 Actors 解释,因此每个 Actor 必须首先使用 Free.resume.
解包命令
这在六年前使用 scalaz where we also used the resume function too, but the Functor for the free monad was easy to create 时效果很好,因为我们使用 case classes 附带了一个可以映射的额外函数参数,例如下面的 k
。
case class GetResource[Rdf <: RDF, A](
uri: Rdf#URI,
k: NamedResource[Rdf] => A) extends LDPCommand[Rdf, A]
但是目前 cats.scala.Free on the cats Free Monad web page don't come with such an argument. And indeed those work nicely when using the interpretation of Free Monads via a natural transformation. I tried this out with a super simple DSL 的例子只有一个案例 class
sealed trait LDPCmd[A]:
def url: Uri
case class Get[T](url: Uri) extends LDPCmd[Response[T]]
然后我可以写 a simple Script which works as expected in the tests using the natural transformation interpretation。
但是对于基于 Actors 的解释,我现在需要解包 free monad 中的每个命令,这些命令使用 Free 的 resume
函数发送给不同的 actor。这需要一个 Functor。但我不清楚函子在哪里可以抓住。即,我在 ???位置在这里
given CmdFunctor: cats.Functor[LDPCmd] with
def map[A, B](fa: LDPCmd[A])(f: A => B): LDPCmd[B] = fa match
case g: Get => ???
问题出在您的数据类型中:Get[T]
不使用 T
,因此除了无操作外无法有意义地映射:g: Get => Get[B](g)
答案似乎是 return 向案例 class 添加额外的功能。在猫聊天频道 Rob Norris 上写道:
The classical encoding of Free[F, *] is a monad only if F is a functor, so you needed the extra Blah => A member for each case. Then we figured out that you could use Free[Coyoneda[F, *], *] and get that machinery for free. Then we figured out how to just bake it into Free, so you don’t need to do any of that most of the time.
But if you need to walk through your computation step by step you do need one of those encodings because F still needs to be a Functor in that case.
他指向的代码是SimMessageSocket.scala。
sealed trait Step[+A]
case class Send[A](m: BackendMessage, k: Unit => A) extends Step[A]
case class Expect[A](h: PartialFunction[FrontendMessage, A]) extends Step[A]
object Step {
implicit val FunctorStep: Functor[Step] =
new Functor[Step] {
def map[A,B](fa: Step[A])(f: A => B): Step[B] =
fa match {
case Send(m, k) => Send(m, k andThen f)
case Expect(h) => Expect(h andThen f)
}
}
}
我正在 cats.free.Free Monad 库的帮助下使用 case classes 编写 DSL。 DSL 将由接收消息的 Actors 解释,因此每个 Actor 必须首先使用 Free.resume.
解包命令这在六年前使用 scalaz where we also used the resume function too, but the Functor for the free monad was easy to create 时效果很好,因为我们使用 case classes 附带了一个可以映射的额外函数参数,例如下面的 k
。
case class GetResource[Rdf <: RDF, A](
uri: Rdf#URI,
k: NamedResource[Rdf] => A) extends LDPCommand[Rdf, A]
但是目前 cats.scala.Free on the cats Free Monad web page don't come with such an argument. And indeed those work nicely when using the interpretation of Free Monads via a natural transformation. I tried this out with a super simple DSL 的例子只有一个案例 class
sealed trait LDPCmd[A]:
def url: Uri
case class Get[T](url: Uri) extends LDPCmd[Response[T]]
然后我可以写 a simple Script which works as expected in the tests using the natural transformation interpretation。
但是对于基于 Actors 的解释,我现在需要解包 free monad 中的每个命令,这些命令使用 Free 的 resume
函数发送给不同的 actor。这需要一个 Functor。但我不清楚函子在哪里可以抓住。即,我在 ???位置在这里
given CmdFunctor: cats.Functor[LDPCmd] with
def map[A, B](fa: LDPCmd[A])(f: A => B): LDPCmd[B] = fa match
case g: Get => ???
问题出在您的数据类型中:Get[T]
不使用 T
,因此除了无操作外无法有意义地映射:g: Get => Get[B](g)
答案似乎是 return 向案例 class 添加额外的功能。在猫聊天频道 Rob Norris 上写道:
The classical encoding of Free[F, *] is a monad only if F is a functor, so you needed the extra Blah => A member for each case. Then we figured out that you could use Free[Coyoneda[F, *], *] and get that machinery for free. Then we figured out how to just bake it into Free, so you don’t need to do any of that most of the time. But if you need to walk through your computation step by step you do need one of those encodings because F still needs to be a Functor in that case.
他指向的代码是SimMessageSocket.scala。
sealed trait Step[+A]
case class Send[A](m: BackendMessage, k: Unit => A) extends Step[A]
case class Expect[A](h: PartialFunction[FrontendMessage, A]) extends Step[A]
object Step {
implicit val FunctorStep: Functor[Step] =
new Functor[Step] {
def map[A,B](fa: Step[A])(f: A => B): Step[B] =
fa match {
case Send(m, k) => Send(m, k andThen f)
case Expect(h) => Expect(h andThen f)
}
}
}