我面临定义与FreeApplicative
一起使用的递归代数的问题。
这是我失败的尝试。
让我们假设我们希望功能标记(组)有效值。
我已经插入了虚拟Print
以获得一些构造函数。
sealed trait Algebra[F[_],T]
case class Print[F[_]](s: String) extends Algebra[F,String]
case class Prefix[F[_],T](p: String, tagged: F[T]) extends Algebra[F,T]
当我尝试定义免费类型FA
时(使用kind-projector)
type FA[T] = FreeApplicative[Algebra[FA,?],T]
然后编译器抱怨
错误:(28,38)涉及FA类型的非法循环引用 类型FA [T] = FreeApplicative [Alg [FA,?],T]
我该如何解决这个问题?
我认为在使用Free
(monads)时,Prefix
的签名可以省略对tagged
的引用。
当monad链接他们的代数调用时,人们可以简单地记住执行路径上的所有前缀调用。
但是对于Applicative
,我看不出如何实现这一点。
答案 0 :(得分:2)
您要求Algebra
的说明参考使用Algebra
构建的免费程序。
您需要定义自己的“递归”免费应用程序:
case class FreeApRec[F[_[_], _], A](fa: FreeApplicative[F[FreeApRec[F, ?], ?], A])
并实现您需要的方法(这将只转发到FreeApplicative
的方法并包装结果)。
Here is the same done for the Free
monad.
您还可以通过更高级别的定点运算符定义上面的FreeApRec
类型,但定义变得不那么透明:
case class HFix[F[_[_], _], A](unfix: F[HFix[F, ?], A])
type FreeApRecF[F[_[_], _], K[_], A] = FreeApplicative[F[K, ?], A]
type FreeApRec[F[_[_], _], A] = HFix[FreeApRecF[F, ?[_], ?], A]
答案 1 :(得分:1)
这不是一个解决方案,而是另一种方法,可以看出为什么FreeApplicative
没有做你想做的事情,以及如何推导出
Tomas Mikula的回答中提到的正确签名。
FreeApplicative[F, A]
的种类是
(* -> *) -> * -> *
这实际上意味着FreeApplicative
采用F
类型* -> *
的仿函数,并再次构建另一个类似FreeApplicative[F, ?]
的仿函数* -> *
。
但是,您的Algebra
不是* -> *
,而是
(* -> *) -> * -> *
并且你想要一个“事物”,比如FreeApRec
从它构建一个类似* -> *
的仿函数,也就是说,FreeApRec
应该是
((* -> *) -> * -> *) -> * -> *
现在,这与Tomas Mikula写下的签名非常吻合:
((* -> *) -> * -> *) -> * -> *
\ / | |
\ / | |
\ / | |
_[_] _ |
\ / |
\ / |
\ / |
\ / |
F[_[_], _] A
\ /
\ /
\ /
\ /
FreeApRec[F[_[_],_],A]
标准FreeApplicative
没有此签名。如果您调用普通函数的“默认类别”,每个人都隐含地使用C
,那么FreeApplicative
会将问题[C, C]
映射到其他仿函数[C, C]
,但您想要[[C,C] x C, C]
到[C, C]
。这又是一个类别 - 理论 - 仿函数,但不是普通的fp - Functor
,因为它有一个不同的类别作为域。