测试带有负数的函数时出现类型错误

Type error when testing a function with a negative number

我正在关注 向您学习 Haskell 非常有用,我已经实施 take':

take' :: (Ord i, Num i) => i -> [a] -> [a]
take' n _
  | n <= 0 = []
take' _ [] = []
take' n (x:xs) = x: take' (n-1) xs

测试功能时:

take' -2 [2]

我得到的不是空列表,而是这条消息:

Non type-variable argument in the constraint: Num (i -> [a] -> [a])
    (Use FlexibleContexts to permit this)
    When checking that ‘it’ has the inferred type
      it :: forall i a t.
            (Num i, Num t, Num (i -> [a] -> [a]), Num ([t] -> i -> [a] -> [a]),
             Ord i) =>
            i -> [a] -> [a]

我按照建议在-2之间添加了一个space,它导致了同样的错误:

*Main> take' - 2 [2]

<interactive>:78:1:
    Non type-variable argument in the constraint: Num (i -> [a] -> [a])
    (Use FlexibleContexts to permit this)
    When checking that ‘it’ has the inferred type
      it :: forall i a t.
            (Num i, Num t, Num (i -> [a] -> [a]), Num ([t] -> i -> [a] -> [a]),
             Ord i) =>
            i -> [a] -> [a]

正如评论中已经讨论的那样,这只是解析规则的问题。您的表达式 take' -2 [2] 确实看起来应该像您想要的那样表示 take' (-2) [2] 。可以说,它应该被这样解析。事实上 GHC 有一个扩展来实现这种行为:

GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
Prelude> :set -XNegativeLiterals 
Prelude> take -2 [2]
[]

然而,默认情况下,Haskell 总是首先尝试解析所有运算符,包括 -,作为 中缀运算符 。在上面的表达式中,- 在左边和右边都有一些东西(尽管间距不一致,但它被忽略了),所以如果没有 -XNegativeLiterals,它最终会被解析为 (take) - (2 [2]),这意味着完全不同的东西。实际上,它完全是伪造的,正如错误消息以一种非常神秘的方式暗示的那样:它想要 Num (i -> [a] -> [a]),即它发现您的代码需要将函数(即 take)视为数字(即,作为减法运算符的参数)。

几乎总是当您看到包含对 Num (Some Compound Type) 的需求的错误时,这意味着在解析级别上已经完全出错了。