带有函数组合的类型推断列表

Dav*_*vid 3 haskell ghci function-composition

我试图使用折叠来取得Haskell中整数之和的平方.但是,我从GHCi那里得到了一个神秘的错误.这是我的单行:

((^2) . foldl) (+) 0 [1..100]
Run Code Online (Sandbox Code Playgroud)

我从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]
Run Code Online (Sandbox Code Playgroud)

我认为问题出在我最后根据此类型声明传入的列表中.

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

任何人都可以给我一些洞察为什么这种类型期望Enum和任何方式明确地转换列表,以便我可以调试此功能?提前致谢.

C. *_*ann 9

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

首先,忽略了消息,考虑的类型foldl(.):

foldl :: (a -> b -> a) -> a -> [b] -> a
(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)

请注意,(.)只使用第一个参数组成.由于currying,具有"多个"参数的函数实际上是一个返回另一个函数的参数的函数.所以在表达式((^2) . foldl)中,foldlis 的"返回类型" a -> [b] -> a就是它试图构成的东西(^ 2).

并且因为错误消息是愚蠢的,它抱怨没有Num实例来构建a -> [b] -> a(^2) :: Num a => a -> a并建议你添加一个.

你想要的是这样的:((^2) . foldl (+) 0).也就是说,使用(懒惰)foldl可能是一个坏主意.最好使用严格foldl'或更好的内置sum功能:(^2) . sum

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


J. *_*son 5

写这个的正确方法是(^2) . foldl (+) 0 :: Num a => [a] -> a(^2) . foldl (+) 0 $ [1..100] :: (Num a, Enum a) => a.当你单独做的时候(^2) . foldl,你试图对第一个返回类型的参数进行平方foldl,这是一个函数a -> [a] -> a.该错误声明了这一点:它没有Num这种函数的实例,因此无法调度适当的幂函数(^).

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

(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)

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

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