以下是“了解你是一个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,但具体是什么呢?
答案 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 ~ a
和m ~ 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并选择Sum
为n
:例如,
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
的子类也不是超类,为什么不重要。