3

我正在尝试使用折叠来取 Haskell 中整数和的平方。但是,我从 GHCi 收到了一个神秘的错误。这是我的单行:

((^2) . foldl) (+) 0 [1..100]

我从 GHCi 得到的是:

Prelude> ((^2) . foldl) (+) 0 [1..100]

<interactive>:19:3:
    No instance for (Num (b0 -> [b0] -> b0))
      arising from a use of `^'
    Possible fix:
      add an instance declaration for (Num (b0 -> [b0] -> b0))
    In the first argument of `(.)', namely `(^ 2)'
    In the expression: (^ 2) . foldl
    In the expression: ((^ 2) . foldl) (+) 0 [1 .. 100]

我认为问题出在我基于此类型声明最后传入的列表中。

Prelude> :t ((^2) . foldl) (+) 0 [1..100]
((^2) . foldl) (+) 0 [1..100]
  :: (Enum b, Num b, Num (b -> [b] -> b)) => b

任何人都可以让我了解为什么这种类型需要一种Enum以及任何方式来显式转换列表以便我可以调试这个函数吗?提前致谢。

4

3 回答 3

9

是的,这仍然是 GHC 产生的最荒谬和无用的错误消息之一。

首先,忽略消息,考虑foldland的类型(.)

foldl :: (a -> b -> a) -> a -> [b] -> a
(.) :: (b -> c) -> (a -> b) -> a -> c

请注意,(.)仅使用第一个参数组成。由于柯里化,具有“多个”参数的函数实际上是一个返回另一个函数的参数的函数。所以在表达式((^2) . foldl)中的“返回类型”foldla -> [b] -> a,这是它试图与 组合(^ 2)

而且由于错误消息是愚蠢的,它抱怨没有Num实例a -> [b] -> a来组合它(^2) :: Num a => a -> a并建议您添加一个。

你想要的是这样的:((^2) . foldl (+) 0). 也就是说,在foldl这里使用 (lazy) 可能是个坏主意。最好使用严格的foldl',或者更好的是,使用内置sum函数:(^2) . sum

此外,Enum类型中提到的约束是无关紧要的,实际上是正确的——Enum类型类提供了用于解释范围表示法的函数。所以(Enum b, Num b) => ...意味着这b是一个可以枚举的数字类型,这正是你需要的表达式[1 .. 100]

于 2013-04-21T05:39:24.350 回答
5

正确的写法是(^2) . foldl (+) 0 :: Num a => [a] -> aor (^2) . foldl (+) 0 $ [1..100] :: (Num a, Enum a) => a。当您自己这样做时(^2) . foldl,您会尝试将第一个返回类型的参数平方foldl,这是一个函数a -> [a] -> a。该错误声明了这一点:它没有Num此类函数的实例,因此无法调度适当的 power function (^)

通常,将组合(.)视为仅适用于单个输入的功能的东西。类型表明了这一点

(.) :: (b -> c) -> (a -> b) -> a -> c

虽然它有更一般的用途,但它们有点难以找到。

foldl所以我的解决方案有效,因为我在编写它之前直接应用了参数。这些参数构成foldl (+) 0 :: Num a => [a] -> a单个输入参数的函数。

于 2013-04-21T05:37:35.877 回答
0

已经给出了很好的答案,但也许写起来更直接

(^2) $ foldl (+) 0 [1..100]

该表达式foldl (+) 0 [1..100]自行工作,如果您试图了解 foldl 的工作原理,这会有所帮助,然后将结果传递给(^2). 写成函数

f list = (^2) $ foldl (+) 0 list

于 2013-04-23T11:55:33.517 回答