给定一个具有固定点的任意数据结构,我们可以在不手动指定所有情况的情况下构造一个单曲面代数吗?
假设我们得到如下数据类型Expr
。使用该recursion-schemes
库,我们可以派生一个基础仿函数,它也ExprF
自动具有Functor
和实例。Foldable
Traversable
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
import Data.Semigroup (Sum(..))
import Data.Functor.Foldable
import Data.Functor.Foldable.TH
import Prelude hiding (fail)
data Expr = Var String
| Const Int
| Add [Expr]
| Mult [Expr]
deriving Show
$(makeBaseFunctor ''Expr)
expr :: Fix ExprF
expr = ana project $ Add [Const 1, Const 2, Mult [Const 3, Const 4], Var "hello"]
现在,假设我们要计算 中的叶子数expr
。我们可以很容易地为这样一个小数据结构写一个代数:
alg (ConstF _) = 1
alg (VarF _) = 1
alg (AddF xs) = sum xs
alg (MulF xs) = sum xs
现在,我们可以调用cata alg expr
,它返回5
正确的结果。
让我们假设Expr
它变得非常庞大和复杂,并且我们不想为所有数据构造函数手动编写案例。怎么cata
知道如何组合所有案例的结果?我怀疑这可能使用Monoid
s,可能与Const
仿函数一起使用(虽然不完全确定最后一部分)。
fail = getSum $ foldMap (const (Sum 1) . unfix) $ unfix expr
fail
返回4
,而我们实际上有5
叶子。我假设问题出在不动点上,因为我们只能剥一层Fix
ing,因此Mult [..]
只能算作一片叶子。
是否可以在不手动指定所有实例的情况下以某种方式通用地折叠整个固定点并以类似Monoid
结构的方式收集结果?我想要的是一种但更通用的方式。foldMap
我有一种感觉,我错过了一些非常明显的东西。