根据 Bartosz Milewski 的文章(一、二),我定义了一个F-Algebra:
(这并不是说我的代码是 Bartosz 思想的准确体现,这只是我对它们的有限理解,任何错误都是我一个人的问题。)
module Algebra where
data Expr a = Branch [a] | Leaf Int
instance Functor Expr where
fmap f (Branch xs) = Branch (fmap f xs)
fmap _ (Leaf i ) = Leaf i
newtype Fix a = Fix { unFix :: a (Fix a) }
branch = Fix . Branch
leaf = Fix . Leaf
-- | This is an example algebra.
evalSum (Branch xs) = sum xs
evalSum (Leaf i ) = i
cata f = f . fmap (cata f) . unFix
我现在几乎可以做任何我想做的事情,例如,对叶子求和:
λ cata evalSum $ branch [branch [leaf 1, leaf 2], leaf 3]
6
这是我专门为这个问题编造的一个人为的例子,但我实际上尝试了一些不那么琐碎的事情(例如评估和简化具有任意数量变量的多项式),它就像一个魅力。One may indeed fold and replace any parts of a structure as one runs a catamorphism through, with a suitably chosen algebra . 所以,我很确定 F-Algebra 包含了 Foldable,它甚至似乎也包含了 Traversable。
现在,我可以根据 F 代数定义可折叠/可遍历实例吗?
在我看来,我不能。
- 我只能在初始 algebra上运行 catamorphism ,这是一个空类型构造函数。而且我给它的代数有一个类型,
a b -> b
而不是a -> b
,也就是说,“in”和“out”类型之间存在函数依赖关系。 - 我
Algebra a => Foldable a
在类型签名中看不到任何地方。如果不这样做,那一定是不可能的。
在我看来,我不能Foldable
用 F 代数来定义,因为一个Expr
必须是 aFunctor
在两个变量中:一个是载体,另一个是值,然后是Foldable
第二个。因此,双函子可能更合适。我们也可以构造一个带有双函子的 F-代数:
module Algebra2 where
import Data.Bifunctor
data Expr a i = Branch [a] | Leaf i
instance Bifunctor Expr where
bimap f _ (Branch xs) = Branch (fmap f xs)
bimap _ g (Leaf i ) = Leaf (g i)
newtype Fix2 a i = Fix2 { unFix2 :: a (Fix2 a i) i }
branch = Fix2 . Branch
leaf = Fix2 . Leaf
evalSum (Branch xs) = sum xs
evalSum (Leaf i ) = i
cata2 f g = f . bimap (cata2 f g) g . unFix2
它像这样运行:
λ cata2 evalSum (+1) $ branch [branch [leaf 1, leaf 2], leaf 3]
9
但我仍然无法定义可折叠。它会有这样的类型:
instance Foldable \i -> Expr (Fix2 Expr i) i where ...
不幸的是,没有得到关于类型的 lambda 抽象,也没有办法同时将隐含的类型变量放在两个地方。
我不知道该怎么办。