我有一个简单的树结构:
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
没有实现,我的代码也不能同时使用mappend
和mempty
。那么这个Foldable
实现如何工作?
答案 0 :(得分:9)
如果您检查foldMap
class Foldable f where
foldMap :: Monoid m => (a -> m) -> f a -> m
您会看到它有一个未绑定的类型m
。通常,当发生这种情况时,意味着m
可能任何,但此处它还会m
与Monoid 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
可以包含的内容。通过设置约束,我们可以获得更多操纵它们的能力。