在描述Data.Foldable的页面上,它说: 可折叠的实例预计将满足以下法律:
foldr f z t = appEndo (foldMap (Endo . f) t ) z
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
fold = foldMap id
请帮助我了解上述法律的运作方式。什么是Endo . f
?
答案 0 :(得分:8)
Endo
是"构成"下的内同胚的幺半群。 appEndo
是此类字段。
newtype Endo a = Endo { appEndo :: a -> a }
-- i.e.
Endo :: (a -> a) -> Endo a
appEndo :: Endo a -> (a -> a)
你可以对待" endomorphism"作为技术上正确的函数术语,输入和输出类型是相同的(a -> a
)。
Endo
是Monoid
的一个实例,其成分为:
instance Monoid (Endo a) where
mempty = Endo id
Endo f `mappend` Endo g = Endo (f . g)
为了使法律更容易理解,让我们使用List类型作为一个具体的例子:
instance Foldable [a] where
foldMap f [] = mempty
foldMap f (x:xs) = f x `mappend` foldMap f xs
-- i.e.
-- foldMap f [a, b, …, w] == f a `mappend` f b `mappend` … `mappend` f w
当我们评估法律的RHS时:
foldMap (Endo . f) t
== foldMap (\x -> Endo (f x)) [a, b, …, w]
== (\x -> Endo (f x)) a `mappend` … `mappend` (\x -> Endo (f x)) w
== Endo (f a) `mappend` Endo (f b) `mappend` … `mappend` Endo (f w)
召回mappend
Endo是作文,所以上面是
== Endo (f a . f b . … . f w)
最后,我们使用appEndo (…) z
取出撰写的函数并将其应用于初始值z
:
appEndo (Endo (f a . f b . … . f w)) z
== (f a . f b . … . f w) z
== f a (f b ( … (f w z) … ))
这正是列表foldr
的定义。
foldl
一个是相似的,Dual
是另一个幺半群实例Dual a `mappend` Dual b == Dual (b `mappend` a)
,而getDual
取出内部幺半群。应该很容易看出它是如何产生foldl
的。
fold
函数将褶皱的褶皱折叠成单个幺半群。再次使用List作为示例,fold
可以实现为
fold [a, b, …, w] == a `mappend` b `mappend` … `mappend` w
可以从foldMap
检索:
foldMap id [a, b, …, w] == id a `mappend` id b `mappend` … `mappend` id w
== a `mappend` b `mappend` … `mappend` w
这显示了如何建议身份fold = foldMap id
。