没有Monoid实例的折叠

时间:2013-11-08 01:49:57

标签: haskell tree monoids

我有一个简单的树结构:

data Tree a = Leaf | Node a (Tree a) (Tree a)

可折叠实施:

import qualified Data.Foldable as F

instance F.Foldable Tree where
  foldMap f Leaf         = mempty
  foldMap f (Node x l r) = F.foldMap f l `mappend`
                           f x           `mappend`
                           F.foldMap f r

即使Monoid没有实现,我的代码也不能同时使用mappendmempty。那么这个Foldable实现如何工作?

1 个答案:

答案 0 :(得分:9)

如果您检查foldMap

的类型
class Foldable f where
  foldMap :: Monoid m => (a -> m) -> f a -> m

您会看到它有一个未绑定的类型m。通常,当发生这种情况时,意味着m可能任何,但此处它还会mMonoid m约束。就在那里Monoid来自。

值得注意的是,如果我们没有Monoid实例,那么很难定义一个返回“可能是任何东西”的值的函数。如果你尝试它,你会发现它几乎是不可能的(没有“作弊”)。

impossible :: Int -> b -- no constraints on `b` at all!
impossible i = ...?

但如果我们对类型

有所了解,那就很容易了
veryPossible  :: Num b => Int -> b
veryPossible  i = fromIntegral i
-- or
veryPossible2 i = fromIntegral (i * i) + fromIntegral i

作为另一个例子,考虑表达式的类型

expr m = mconcat [m <> m <> mempty, mempty <> m]

因为这个表达式是基于一些未知值m构建的,并且只使用 Monoid类中的函数或它们的派生词,所以它的类型反映了这一点。最常见的expr类型是

expr :: Monoid m => m -> m

此处,m是一个自由类型变量,被约束为某些 Monoid


foldMap允许您使用Monoid函数的原因是因为它明确约束了类型签名中的m可以包含的内容。通过设置约束,我们可以获得更多操纵它们的能力。