我正在查看Haskell中的Foldable
类。其中两个方法fold
,foldMap
需要一个Monoid实例。但foldr
或foldl
没有任何此类限制。
fold :: Monoid m => t m -> m
foldMap :: Monoid m => (a -> m) -> t a -> m
foldr :: (a -> b -> b) -> b -> t a -> b
foldl :: (b -> a -> b) -> b -> t a -> b
要使foldr
/ foldl
的结果等效,是否应该将给定的折叠函数限制为关联?是否有任何例子表明foldr / foldl的结果在同一个列表中是不同的?
Foldable实例不应该包裹Monoidal值吗?或者可折叠更一般?
答案 0 :(得分:5)
要使
foldr
/foldl
的结果等效,是否应该限制给定的折叠函数是关联的?是否有任何示例foldr
/foldl
的结果在同一列表中有所不同?
是。如果你传递一个非关联函数(如减法(-)
),你将获得不同的结果。正如你正确地指出,没有Monoid
实例与(-)
之类的东西相对应。
但那是设计上的。对Foldable
和foldr
必须采用关联函数的foldl
个实例没有此类限制。在某些情况下,您可能希望使用减法等方法进行折叠。 Foldable f
的实例更关心约束f
可以执行的操作。法律特别是:
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
-- if f is a Functor
foldMap f = fold . fmap f
foldMap f . fmap g = foldMap (f . g)
你可以在消息来源中看到foldr
默认使用newtype Endo a = Endo (a -> a)
内同态monoid做了一些聪明的事情:
-- | Right-associative fold of a structure.
--
-- @'foldr' f z = 'Prelude.foldr' f z . 'toList'@
foldr :: (a -> b -> b) -> b -> t a -> b
foldr f z t = appEndo (foldMap (Endo #. f) t) z
用可能非幺半形的f
和z
构建一个幺半折叠。
最终问题的答案"为什么Monoid不是必需的?"是非常无聊的"因为它更实用,最终没有必要。"
有关详细信息,请参阅启动所有内容的论文Applicative Programming with Effects。