来自http://research.microsoft.com/en-us/um/people/emeijer/Papers/meijer94more.pdf的第3页:
一般情况下,在构图
下封闭症状是不正确的
catamorphisms在什么条件下构成catamorphism?更具体地说(假设我正确地理解了陈述):
假设我有两个基础仿函数F
和G
,并为每个仿函数foldF :: (F a -> a) -> (μF -> a)
和foldG :: (G a -> a) -> (μG -> a)
折叠。
现在假设我有两个代数a :: F μG -> μG
和b :: G X -> X
。
组合(foldG b) . (foldF a) :: μF -> X
何时是一个变形?
编辑:我猜测,基于dblhelix的扩展答案:outG . a :: F μG -> G μG
必须是某些自然转换μG
η :: F a -> G a
的组件。我不知道这是否正确。 (编辑2:正如科拉指出的那样,这已足够,但并非必要。)
编辑3: Wask Thornton在Haskell-Cafe上补充说:“如果你有合适的分配属性(正如科拉所建议的那样),那么事情就会适用于特定情况。但是,拥有正确的分配性属性通常相当于某种适当相关类别的自然转换;因此,只是将问题推迟到适当相关的类别是否总是存在,以及我们是否可以将“适当相关”的含义形式化。“
答案 0 :(得分:5)
何时为组合物(fold2g)。 (fold1 f)::μF1 - >一个catamorphism?
当存在F1
- 代数h :: F1 A -> A
时fold1 h = fold2 g . fold1 f
。
要看到catamorphisms一般不在组合下关闭,请考虑以下类型级定点,代数和变形的一般定义:
newtype Fix f = In {out :: f (Fix f)}
type Algebra f a = f a -> a
cata :: Functor f => Algebra f a -> Fix f -> a
cata phi = phi . fmap (cata phi) . out
对于catamorphisms撰写我们需要
algcomp :: Algebra f (Fix g) -> Algebra g a -> Algebra f a
现在尝试编写此功能。它需要两个函数作为参数(分别为f (Fix g) -> Fix g
和g a -> a
类型)和类型f a
的值,并且需要生成类型a
的值。你会怎么做?要生成a
类型的值,您唯一的希望是应用g a -> a
类型的函数,但是我们陷入困境:我们无法将f a
类型的值转换为类型g a
的值,有吗?
我不确定这是否对你的目的有用,但是一个条件的例子就是我们可以从第二个cata的结果到第二个cata的固定点有一个态射。算符:
algcomp' :: (Functor f, Functor g) =>
(a -> Fix g) -> Algebra f (Fix g) -> Algebra g a -> Algebra f a
algcomp' h phi phi' = cata phi' . phi . fmap h
答案 1 :(得分:4)
(免责声明:这超出了我的专业领域。我相信我是正确的(在不同的地方提供了警告),但是......自己验证。)
可以将catamorphism视为用其他函数替换数据类型的构造函数的函数。
(在本例中,我将使用以下数据类型:
data [a] = [] | a : [a]
data BinTree a = Leaf a | Branch (BinTree a) (BinTree a)
data Nat = Zero | Succ Nat
)
例如:
length :: [a] -> Nat
length = catamorphism
[] -> 0
(_:) -> (1+)
(可悲的是,catamorphism {..}
语法在Haskell中不可用(我在Pola中看到类似的东西)。我一直想为它写一个quasiquoter。)
那么,length [1,2,3]
是什么?
length [1,2,3]
length (1 : 2 : 3 : [])
length (1: 2: 3: [])
1+ (1+ (1+ (0 )))
3
那就是说,由于后来会变得明显的原因,将它定义为平凡的等价物是更好的:
length :: [a] -> Nat
length = catamorphism
[] -> Zero
(_:) -> Succ
让我们再考虑一些示例性的catamorphisms:
map :: (a -> b) -> [a] -> b
map f = catamorphism
[] -> []
(a:) -> (f a :)
binTreeDepth :: Tree a -> Nat
binTreeDepth = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + max a b
binTreeRightDepth :: Tree a -> Nat
binTreeRightDepth = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + b
binTreeLeaves :: Tree a -> Nat
binTreeLeaves = catamorphism
Leaf _ -> 1
Branch -> (+)
double :: Nat -> Nat
double = catamorphism
Succ -> Succ . Succ
Zero -> Zero
其中许多可以很好地组合以形成新的catamorphisms。例如:
double . length . map f = catamorphism
[] -> Zero
(a:) -> Succ . Succ
double . binTreeRightDepth = catamorphism
Leaf a -> Zero
Branch -> \a b -> Succ (Succ b)
double . binTreeDepth
也有效,但在某种意义上它几乎是一个奇迹。
double . binTreeDepth = catamorphism
Leaf a -> Zero
Branch -> \a b -> Succ (Succ (max a b))
这只能起作用,因为double
分布在max
上......这纯属巧合。 (对于double . binTreeLeaves
也是如此。)如果我们将max
替换为不能很好地加倍的东西......那么,让我们自己定义一个新朋友(不相处和其他人一样)。对于double
未分发的二元运算符,我们将使用(*)
。
binTreeProdSize :: Tree a -> Nat
binTreeProdSize = catamorphism
Leaf _ -> 0
Branch -> \a b -> 1 + a*b
让我们尝试为两个组合构成两个构造的充分条件。很明显,任何catamorphism都很乐意与length
,double
和map f
组成,因为它们会在不查看子结果的情况下生成数据结构。例如,在length
的情况下,您只需将Succ
和Zero
替换为您想要的内容,就可以获得新的catamorphism。
除此之外,事情变得更加复杂。让我们区分正常的构造函数参数和“递归参数”(我们将使用%符号标记)。所以Leaf a
没有递归参数,但Branch %a %b
没有。让我们使用构造函数的“递归固定”一词来指代它具有的递归参数的数量。 (我已经完成了这两个术语!我不知道什么是合适的术语,如果有的话!在其他地方使用它们时要小心!)
如果第一个catamorphism映射到零递归固定构造函数,一切都很好!
a | b | cata(b.a)
===============================|=========================|================
F a %b %c .. -> Z | Z -> G a b .. | True
如果我们将孩子直接映射到一个新的构造函数,我们也很好。
a | b | cata(b.a)
===============================|=========================|=================
F a %b %c .. -> H %c %d .. | H %a %b -> G a b .. | True
如果我们将一个构造函数映射到递归固定...
a | b | cata(b.a)
===============================|=========================|=================
F a %b %c .. -> A (f %b %c..) | A %a -> B (g %a) | Implied by g
| | distributes over f
但它不是iff。例如,如果g1
g2
存在g (f a b..) = f (g1 a) (g2 b) ..
,那也可以。
从这里开始,我希望这些规则会变得更加混乱。
答案 2 :(得分:3)
Catamorphisms将数据结构解构为结果值。因此,一般来说,当你应用一个变形时,结果是完全不同的,你不能对它应用另一个变形。
例如,对[Int]
的所有元素求和的函数是一个catamorphism,但结果是Int
。没有办法如何在其上应用另一个变形。
但是,某些特殊的catamorphisms会创建与输入相同类型的结果。一个这样的例子是map f
(对于某些给定的函数f
)。虽然它解构了原始结构,但它也会创建一个新列表作为结果。 (实际上,map f
既可以被视为一种变形也可以被视为anamorphism。)因此,如果你有这样一类特殊的变形,你可以将它们组合起来。
答案 3 :(得分:2)
如果我们考虑语义等价,当第一个是一个hylomorphism时,两个catamorphisms的组成是一个catamorphism:
cata1 . hylo1 = cata2
例如(Haskell):
sum . map (^2) = foldl' (\x y -> x + y^2) 0