我如何理解可折叠实例的法律?

时间:2017-04-26 23:02:40

标签: haskell

在描述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

1 个答案:

答案 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)。

EndoMonoid的一个实例,其成分为:

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