GHCi中的类型推断与手动签名

时间:2011-12-14 15:41:01

标签: haskell type-inference ghc ghci

当我输入

:t map length . sum

进入GHCi,它说类型将是:

map length . sum :: Num [[a]] => [[[a]]] -> [Int]

但是,如果我创建一个包含

的文件type-test.hs
x :: Num [[a]] => [[[a]]] -> [Int]
x = map length . sum

ghc和ghci都在抱怨:

type-test.hs:1:1:
    Non type-variable argument in the constraint: Num [[a]]
    (Use -XFlexibleContexts to permit this)
    In the type signature for `x': x :: Num [[a]] => [[[a]]] -> [Int]

为什么ghci允许我推断出这种类型(使用:t),什么时候没有启用FlexibleContexts?

5 个答案:

答案 0 :(得分:10)

首先,让我们澄清一件事。如果我们在GHCi中定义函数而不是查询类型会发生什么?

> let x = map length . sum :: (Num [[a]]) => [[[a]]] -> [Int]
<interactive>:0:9:
    Non type-variable argument in the constraint: Num [[a]]
    (Use -XFlexibleContexts to permit this)
    In an expression type signature: Num [[a]] => [[[a]]] -> [Int]

等等。换句话说,同样的事情。如果我们让GHCi推断出定义的类型怎么办?

> let x = map length . sum
<interactive>:0:22:
    No instance for (Num [[a0]])
      arising from a use of `sum'
    Possible fix: add an instance declaration for (Num [[a0]])
    In the second argument of `(.)', namely `sum'
    In the expression: map length . sum

这与加载包含没有类型签名的定义的文件大致相同。

这一切的结果是什么?好吧,想想它告诉你需要什么扩展的事实。 GHC能够识别类型的类型,即使它默认拒绝该类型。我几乎不希望GHC使用完全不同的类型检查器,具体取决于所使用的扩展组合,因此似乎很容易得出结论,除了相关的扩展被禁用之外,有问题的类型被拒绝。

GHCi中的:t命令不是编译过程的一部分 - 它是类型检查和推理系统的热线,让您询问假设代码的类型。当一个更通用的类型仍然可以提供信息时,没有明显的理由让它基于扩展来任意限制自己,原因与上面的错误消息告诉你use -XFlexibleContexts to permit this而不仅仅是syntax error in type constraint相同。< / p>


除了可能更有趣之外,还有一些情况是编译器会愉快地接受推断类型,但由于以下几个原因之一,实际上无法明确写出推断类型。

例如,禁用单态限制将允许您的示例推断其类型(匹配:t所说的内容),尽管该类型需要手动编写扩展名。

另一个例子是函数定义的where子句中的定义,它使用父函数的多态参数。它们自己的类型不是多态的,由外部作用域中接收的参数决定,但父函数签名中的类型变量不在where子句¹的范围内。可能还有其他例子。

¹如果需要,父母签名中的类型变量可以带有ScopedTypeVariables扩展名和明确的forall

答案 1 :(得分:7)

(这不能回答你原来的问题,而是用代码解决问题)

这些错误暗示您编写的代码可能不是您的意思。这段代码:

map length . sum

表示“取我的数字列表,然后求它,然后计算结果数字的每个元素的长度(??)。”这毫无意义。

你可能意味着:

sum . map length

表示“获取我的列表列表,计算每个元素的长度,并计算长度。”

错误消息本身意味着,因为sum返回一个数字,即sum的类型为Num n => [n] -> n,然后您尝试使用map length ,类型Num m => [[a]] -> [m],编译器试图说n == [[a]]使类型匹配,一切都从那里走下坡路。

答案 2 :(得分:5)

另一种看待它的方式:

Haskell告诉您,您的表达式有一个有效类型,即[[[a]]] -> [Int],如果[[a]]只是Num的实例。

这就像有些女孩告诉你,当地狱冻结时,她会和你一起出去。你不会抱怨她答应和我一起出去尽管地狱可以冻结这个世界没有给出可能性,你呢?你宁愿注意到她只是或多或少地礼貌地说

答案 3 :(得分:1)

问题是该函数是完全可以接受的,但是它的最通用类型不允许由语言语法指定。 ghci在这种情况下可以做些什么?我只能想到两个选项,或者给出一个只能在启用某个扩展名的情况下指定的类型,或者给出错误。提出错误而不提及可以通过启用扩展来修复它似乎对我来说不是很理想。只有:t报告推断的最常规类型更简单,很可能在实现该功能时,没有人想到这样的情况。

答案 4 :(得分:0)