Haskell foldMap采用了错误的参数类型?
以下是“向您学习”Haskell的一些示例:Haskell foldMap采用了错误的参数类型?,haskell,monoids,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 `mappen
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的第一个参数是一个返回幺半群的函数,但是Num不是幺半群的子类,所以这里肯定不限定(+)?但从测试来看,它似乎很合适
而z
在这里有点神秘,它应该是一个幺半群,但具体是什么 “Num a=>”是一个typeclass约束,它指示(+)的参数和结果必须至少是typeclass“Num”的成员,但它们也可以是任何其他typeclass的完美成员(在本例中为Monoid)
关于z,有两个标准类型可以满足这些约束(同时是Num和Monoid的一部分)。它们是在Data.Monoid中定义的和和和积
为了回答您的问题,求和树或乘积树将通过类型检查并工作,您定义为typeclasses Num和Monoid一部分的任何其他类型也将通过类型检查并工作。因此,让我们明确一下这里发生的情况。我们应该将这两种类型统一起来:
Num a => a -> a -> a
Monoid m => b -> m
这可以通过设置b~a
和m~a->a
来实现,从而
(Num a, Monoid (a -> a)) => a -> a -> a
此外,函数还有一个幺半群实例:
instance Monoid b => Monoid (a -> b) where
mempty = \_ -> mempty
mappend f g = \x -> f x `mappend` g x
(暂且不谈为什么这个实例有用。)这意味着,事实上,为了满足Monoid(a->a)
约束,我们只需要确保返回类型--a
--是一个Monoid,因此我们现在为(+)
找到了这个类型:
对于折叠贴图(+)
,此类型为:
其行为是遍历树,(部分)对树的每个元素应用(+)
,然后使用mappend
组合所有结果。因此,小示例树节点3(节点1为空)(节点6为空)
将产生如下结果:
(mempty `mappend` (+) 1 `mappend` mempty) `mappend`
(+) 3 `mappend`
(mempty `mappend` (+) 6 `mappend` mempty)
为了简洁起见,让我们把它写成mconcat[(1+),(3+),(6+)
,尽管这有点含糊其辞。在较大的示例树上运行foldMap(+)
将是mconcat[(1+),(3+),(6+),(5+),(8+),(9+),(10+)
现在,通过计算函数上的幺半群实例,我们发现mconcat[f,g,h]=\x->mconcat[fx,gx,hx]
,等等,还有更多的函数。所以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
;希望您能看到实际结果与您的预期有显著差异(以及原因)
如果我们想做的是把一棵树的所有元素相加,我们实际上可以意外地完成这项工作。我们可以特别选择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
既不是Monoid
的子类也不是超类并不重要,您可能会发现的这段输出:i Monoid
很有趣:实例Monoid b=>Monoid(a->b)
,虽然它没有直接回答你的问题:你几乎肯定是指F.foldMap Sum
而不是F.foldMap(+)
。。。或者可能是F.foldr(+)0
。请注意F.foldMap(+)
可以进行类型检查,但它不会对树的元素求和。
\n -> mconcat [1+n, 3+n, 6+n, 5+n, 8+n, 9+n, 10+n]
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]