简体   繁体   English

使用 catamorphism 忘记 Cofree 注释

[英]Forgetting Cofree annotations using a catamorphism

I have an AST that I'm annotating using Cofree :我有一个 AST,我正在使用Cofree注释:

data ExprF a
  = Const Int
  | Add a
        a
  | Mul a
        a
  deriving (Show, Eq, Functor)

I use type Expr = Fix ExprF to represent untagged ASTs, and type AnnExpr a = Cofree ExprF a to represent tagged ones.我使用type Expr = Fix ExprF来表示未标记的 AST,并使用type AnnExpr a = Cofree ExprF a来表示标记的。 I've figured out a function to transform tagged ASTs into untagged ones by throwing away all the annotations:我已经找到了一个函数,通过丢弃所有注释将标记的 AST 转换为未标记的 AST:

forget :: Functor f => Cofree f a -> Fix f
forget = Fix . fmap uncofree . unwrap

This looks like it might be some sort of catamorphism (I'm using the definition from Kmett's recursion-schemes package).这看起来可能是某种变形(我使用的是 Kmett 的recursion-schemes包中的定义)。

cata :: (Base t a -> a) -> t -> a
cata f = c where c = f . fmap c . project

I'd think the above rewritten using a catamorphism would look something like this, but I can't figure out what to put for alg to make it typecheck.我认为上面使用 catamorphism 重写的内容看起来像这样,但我无法弄清楚为alg放置什么来使其类型检查。

forget :: Functor f => Cofree f a -> Fix f
forget = cata alg where
  alg = ???

Any help figuring out if this really is a cata/anamorphism, and some intuition for why it is/isn't would be greatly appreciated.任何帮助确定这是否真的是 cata/anamorphism,以及为什么它是/不是的一些直觉将不胜感激。

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.:<)

Explanation解释

Looking only at the types, we can show that there is really only one way to implement forget .只看类型,我们可以证明实际上只有一种方法可以实现forget Let's start with the type of cata :让我们从cata的类型开始:

cata :: Recursive t => (Base t b -> b) -> t -> b

Here t ~ Cofree fa and the type instance of Base for Cofree gives:这里t ~ Cofree faBase for Cofree类型实例给出:

type instance Base (Cofree f a) = CofreeF f a

Where CofreeF is: CofreeF在哪里:

data CoFreeF f a b = a :< f b
-- N.B.: CoFree also defines a (:<) constructor so you have to be
-- careful with imports.

ie, a fancy pair type.即,花哨的配对类型。 Let's replace it with an actual pair type to make things clearer:让我们用实际的配对类型替换它,以使事情更清楚:

cata :: Functor f => ((a, f b) -> b) -> Cofree f a -> b

Now we're really specializing cata with a more concrete b , namely Fix f :现在我们真的专门用一个更具体的b专门化cata ,即Fix f

-- expected type of `cata` in `forget`
cata :: Functor f => ((a, f (Fix f)) -> Fix f) -> Cofree f a -> Fix f

forget is parametric in a and f , so the function we give cata can do nothing with the a in the pair, and the only sensible way to implement the remaining f (Fix f) -> Fix f is the Fix wrapper. forgetaf是参数化的,所以我们给cata的函数不能对a对中的a做任何事情,实现剩余f (Fix f) -> Fix f的唯一明智方法是Fix包装器。

Operationally, Fix is the identity, so (\\(_ :< z) -> Fix z) is really (\\(_ :< z) -> z) which corresponds to the intuition of removing the annotation, ie, the first component of the pair (_ :< z) .操作上, Fix是恒等式,所以(\\(_ :< z) -> Fix z)真的是(\\(_ :< z) -> z)这对应于去除注解的直觉,即第一个组件对(_ :< z)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM