【发布时间】:2022-01-21 00:53:34
【问题描述】:
我从这种类型开始用于带有标记节点的叶值树:
type Label = String
data Tree a = Leaf Label a
| Branch Label [Tree a]
我想在这棵树上写一些折叠,它们都采用变态的形式,所以让recursion-schemes 为我做递归遍历:
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable, TemplateHaskell, TypeFamilies #-}
import Data.Functor.Foldable.TH (makeBaseFunctor)
import Data.Functor.Foldable (cata)
type Label = String
data Tree a = Leaf Label a
| Branch Label [Tree a]
makeBaseFunctor ''Tree
allLabels :: Tree a -> [Label]
allLabels = cata go
where go (LeafF l _) = [l]
go (BranchF l lss) = l : concat lss
一切都很好:我们可以遍历一棵树:
λ> allLabels (Branch "root" [(Leaf "a" 1), Branch "b" [Leaf "inner" 2]])
["root","a","b","inner"]
但是 Tree 的定义有点笨拙:每个数据构造函数都需要单独处理 Label。对于像 Tree 这样的小型结构来说,这并不算太糟糕,但是如果有更多的构造函数,那就太麻烦了。所以让我们让标签成为它自己的层:
data Node' a = Leaf' a
| Branch' [Tree' a]
data Labeled a = Labeled Label a
newtype Tree' a = Tree' (Labeled (Node' a))
makeBaseFunctor ''Tree'
makeBaseFunctor ''Node'
太好了,现在我们的 Node 类型代表了一个没有标签的树的结构,Tree' 和 Labeled 一起用标签来装饰它。但我不再知道如何将cata 与这些类型一起使用,即使它们与原始Tree 类型同构。 makeBaseFunctor 没有看到任何递归,所以它只定义了与原始类型相同的基本函子:
$ stack build --ghc-options -ddump-splices
...
newtype Tree'F a r = Tree'F (Labeled (Node' a))
...
data Node'F a r = Leaf'F a | Branch'F [Tree' a]
就像,公平地说,我也不知道我希望它生成什么:cata 期望一个单一类型进行模式匹配,当然它不能合成一个组合我的两种类型。
那么这里的计划是什么?如果我定义自己的 Functor 实例,是否有一些对 cata 的改编可以在这里工作?或者定义这种类型的更好方法,避免重复处理 Label 但仍然是自递归而不是相互递归?
我认为这个问题可能与Recursion schemes with several types 有关,但我不明白那里的答案:Cofree 对我来说到目前为止很神秘,我不知道它是问题的本质还是只是一个问题使用的部分表示;并且该问题中的类型不是完全相互递归的,所以我不知道如何将那里的解决方案应用于我的类型。
【问题讨论】: