4

我正在尝试编写自己的 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 出了什么问题?

4

2 回答 2

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

遵循类型

Andrew 和 Lee 已经给了你一个高层次的解释,但也许我可以给你另一个观点:

让我们按照类型来得到这个答案:

我们想要一个函数f a -> m,其中m是一个幺半群并且f是一个函子。此外,我们还有一个函数g :: a -> m可以用来从一些a到幺半群 - 很好。

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

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

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

但是等等:我们可以将 aa变成musing g- 但 thea被打包成f... dang。

哦等等:我们可以把 af a变成f musing 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)

这是一个使用Sumand的简单示例[1..4]

λ> foldMap Sum [1..4]
Sum {getSum = 10}

这对我来说似乎很好。

于 2014-10-10T04:43:27.607 回答
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

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

于 2014-10-09T21:35:09.867 回答