我有一个使用Cofree
进行注释的AST:
data ExprF a
= Const Int
| Add a
a
| Mul a
a
deriving (Show, Eq, Functor)
我用type Expr = Fix ExprF
代表未标记的AST,type AnnExpr a = Cofree ExprF a
代表标记的AST。我想出了一个通过丢弃所有注释将带标签的AST转换为无标签的AST的功能:
forget :: Functor f => Cofree f a -> Fix f
forget = Fix . fmap uncofree . unwrap
这看起来可能是某种形式的变形(我使用的是Kmett的recursion-schemes包中的定义)。
cata :: (Base t a -> a) -> t -> a
cata f = c where c = f . fmap c . project
我认为以上使用同构方法重写的内容看起来像这样,但是我不知道要为alg
进行类型检查而放什么。
forget :: Functor f => Cofree f a -> Fix f
forget = cata alg where
alg = ???
任何帮助弄清楚这是否真的是催化剂/变形,对于为什么/不是这种原因的一些直觉将不胜感激。
答案 0 :(得分:5)
forget :: Functor f => Cofree f a -> Fix f
forget = cata (\(_ :< z) -> Fix z)
-- (Control.Comonad.Trans.Cofree.:<)
-- not to be confused with
-- (Control.Comonad.Cofree.:<)
仅查看类型,我们可以证明实际上只有一种方法可以实现forget
。
cata :: Recursive t => (Base t b -> b) -> t -> b
t ~ Cofree f a
和type instance of Base
for Cofree
给出:
type instance Base (Cofree f a) = CofreeF f a
CofreeF
在哪里:
data CoFreeF f a b = a :< f b
-- N.B.: CoFree also defines a (:<) constructor so you have to be
-- careful with imports.
,即花式对类型。让我们将其替换为实际的对类型,以使事情更清楚:
cata :: Functor f => ((a, f b) -> b) -> Cofree f a -> b
现在,我们实际上是在以更具体的cata
(即a
)为特色Fix f
:
-- expected type of `cata` in `forget`
cata :: Functor f => ((a, f (Fix f)) -> Fix f) -> Cofree f a -> Fix f
forget
在a
和f
中是参数化的,因此我们给cata
赋予的功能对于成对的a
无效,并且唯一f (Fix f) -> Fix f
包装器是实现其余Fix
的明智方式。
操作上,Fix
是标识,所以(\(_ :< z) -> Fix z)
实际上是(\(_ :< z) -> z)
,它对应于删除注释的直觉,即(_ :< z)
对中的第一个组件