在Haskell中编写foldMap

时间:2014-10-09 20:11:50

标签: haskell functor foldable

我正在尝试编写自己的foldMap函数作为学习Haskell

的例外

目前它看起来像这样

class Functor f => Foldable f where
    fold    :: Monoid m =>             f m -> m
    foldMap :: Monoid m => (a -> m) -> f a -> m
    foldMap g a = fold (<>) mempty (fmap g a)

但是在编译时会出现以下错误

Could not deduce (Monoid ((f m -> m) -> fm -> m)) arising from use of 'fold'
from the context (Foldable f) bound by the class declaration for 'Foldable' at (file location)
or from (Monoid m) bound by the type signature for foldMap :: Monoid m => (a -> m) -> f a -> m at (file location
In the expression fold (<>) mempty (fmap g a)
In an equation for 'foldMap':
     foldMap g a = fold (<>) mempty (fmap g a)

我无法弄清楚编译器试图告诉我这个错误的内容,有人能告诉我我的foldMap出了什么问题吗?

2 个答案:

答案 0 :(得分:10)

也许我们应该用实际的解决方案来回答:

我希望现在很清楚,这是一个可能的定义:

class Functor f => Foldable f where
    fold    :: Monoid m =>             f m -> m
    foldMap :: Monoid m => (a -> m) -> f a -> m
    foldMap g a = fold $ fmap g a

按照类型

安德鲁和李已经给了你一个高级别的解释,但也许我可以给你另一个观点:

按照类型来回答这个答案:

我们想要一个函数f a -> m,其中m是一个幺半群,f是一个函子。另外我们有一个函数g :: a -> m我们可以用来从一些a进入monoid - 很好。

现在我们得到一些额外的功能:

    来自我们班级的
  • fold :: f m -> m
  • 来自Functor fmap :: (a -> b) -> f a -> f b
  • f

好的,我们现在需要f a -> m,如果只有am,那么我们可以使用fold ... dang。

但是等一下:我们可以使用am变成g - 但a被打包到f ... dang。< / p>

哦等等:我们可以使用f af m变成fmap .... ding-ding-ding

所以,让我们这样做:

  • f a变为f mfmap g a
  • 使用折叠:fold (fmap g a)

或使用$

foldMap g a = fold $ fmap g a

例如

让我们尝试一下,我们可以尝试:

module Foldable where

import Data.Monoid

class Functor f => Foldable f where
    fold    :: Monoid m => f m -> m
    foldMap :: Monoid m => (a -> m) -> f a -> m
    foldMap g a = fold $ fmap g a

instance Foldable [] where
  fold []     = mempty
  fold (x:xs) = mappend x (fold xs)

这是一个使用Sum[1..4]

的简单示例
λ> foldMap Sum [1..4]
Sum {getSum = 10}

对我来说似乎很好。

答案 1 :(得分:0)

Monoid有两个函数mappendmempty,您可以使用(<>)代替mappend

类型规范是可行的,因为编译器根据数据类型为函数插入适当的定义,因此(很高兴)不需要传递相关函数。

您犯的错误是不必要地传递您正在使用的Monoid函数。

例如,如果我定义了一个函数来测试某个列表是否在这样的列表中:

isin :: Eq a => a -> [a] -> Bool
isin equalityFunction a list = any (equalityFunction a) list

我不必要地尝试将equalityFunction作为参数传递,并且类型签名与它不匹配。

相反,我应该定义

isin :: Eq a => a -> [a] -> Bool
isin a list = any (== a) list

使用Eq类型类中定义的相等函数的标准名称。

同样,您既不需要也不应该传递(<>)empty个参数。