foldMap采用错误的参数类型?

时间:2015-03-29 20:39:24

标签: haskell monoids

以下是“了解你是一个Haskell”的一些例子:

import qualified Data.Foldable as F

data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)  

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

testTree = Node 5  
            (Node 3  
                (Node 1 Empty Empty)  
                (Node 6 Empty Empty)  
            )  
            (Node 9  
                (Node 8 Empty Empty)  
                (Node 10 Empty Empty)  
            )  

测试它:

> let z = F.foldMap (+) testTree
> :t F.foldMap
F.foldMap :: (Monoid m, F.Foldable t) => (a -> m) -> t a -> m
> :t (+)
(+) :: Num a => a -> a -> a
> :t z
z :: (Monoid a, Num a) => a -> a

F.foldable的第一个参数是一个返回Monoid的函数,但是Num不是Monoid的子类,所以(+)肯定不符合这里的条件?但从测试来看似乎很合适。

z在这里有点神秘,它应该是一个Monoid,但具体是什么呢?

2 个答案:

答案 0 :(得分:2)

" Num a =>"是一个类型类约束,它表示(+)的参数和结果必须是至少类型类的成员" Num",但它们可以是任何其他类型类的完美成员也是(在这种情况下,Monoid)。

关于z,有几种标准类型可以满足这些约束(同时是Num和Monoid的一部分)。这些是Sum和Product,在Data.Monoid中定义。

要回答您的问题,求和树或产品将通过类型检查和工作,因此您将定义为类型为Num和Monoid的任何其他类型。

答案 1 :(得分:1)

所以,让我们清楚一下这里发生了什么。我们应该统一这两种类型:

Num a => a -> a -> a
Monoid m => b -> m

这可以通过设置b ~ am ~ a -> a来完成,从而产生

(Num a, Monoid (a -> a)) => a -> a -> a

此外,函数有一个monoid实例:

instance Monoid b => Monoid (a -> b) where
    mempty = \_ -> mempty
    mappend f g = \x -> f x `mappend` g x

(让我们先说明为什么这个实例暂时有用。)这意味着,事实上,为了满足Monoid (a -> a)约束,我们只需要确保返回类型 - {{ 1}} - 是一个幺半群,因此我们现在为a

达到这种类型
(+)

(Num a, Monoid a) => a -> a -> a 的此类型:

foldMap (+)

这样做是为了遍历树,(部分地)将(Monoid a, F.Foldable t) => t a -> a -> a 应用于树的每个元素,然后使用(+)来组合所有结果。因此,小示例树mappend将产生如下内容:

Node 3 (Node 1 Empty Empty) (Node 6 Empty Empty)

对于紧凑性,让我们将其写为(mempty `mappend` (+) 1 `mappend` mempty) `mappend` (+) 3 `mappend` (mempty `mappend` (+) 6 `mappend` mempty) ,尽管这会稍微捏造一下。在较大的示例树上运行mconcat [(1+), (3+), (6+)]将为foldMap (+)

现在,弄清楚函数上的monoid实例说的是什么,我们发现mconcat [(1+), (3+), (6+), (5+), (8+), (9+), (10+)],等等有更多的函数。所以mconcat [f,g,h] = \x -> mconcat [f x, g x, h x]出现在

foldMap (+) testTree

可能你期待\n -> mconcat [1+n, 3+n, 6+n, 5+n, 8+n, 9+n, 10+n] 出现foldMap (+) testTree之类的东西;希望你能看到实际结果与你的期望(以及为什么)显着不同。

如果我们想要做的是将树的所有元素相加,我们实际上可以意外地完成这项工作。我们可以专门选择1+3+6+5+8+9+10 monoid并选择Sumn:例如,

0

这是一种非常复杂的方法来获取树中元素的总和。但希望这可以解释为什么你写的不仅仅是一个类型错误; getSum $ foldMap (+) testTree 0 = { by our computations above } getSum $ (\n -> mconcat [1+n, 3+n, 6+n, 5+n, 8+n, 9+n, 10+n]) 0 = { beta reduction } getSum $ mconcat [1+0, 3+0, 6+0, 5+0, 8+0, 9+0, 10+0] = { n+0 = n } getSum $ mconcat [1, 3, 6, 5, 8, 9, 10] = { definition of mconcat for Sum } getSum $ sum [1, 3, 6, 5, 8, 9, 10] = { playing fast and loose with polymorphic literals } sum [1, 3, 6, 5, 8, 9, 10] 的含义是什么;为什么z既不是Num的子类也不是超类,为什么不重要。