当我打字时
: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]
当未启用 FlexibleContexts 时,为什么 ghci 允许我推断此类型(使用 :t)?
最佳答案
首先,让我们澄清一件事。如果我们在 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 会根据所用扩展的组合使用完全不同的类型检查器,因此似乎很容易得出结论,除了相关扩展被禁用之外,无缘无故地拒绝了有问题的类型。
:t
GHCi 中的命令不是编译过程的一部分——它是类型检查和推理系统的热线,让您询问假设代码的类型。当更通用的类型仍然可以提供信息时,它没有明显的理由根据扩展来任意限制自己,原因与上面的错误消息告诉您的原因相同 use -XFlexibleContexts to permit this
而不仅仅是syntax error in type constraint
.除了可能更有趣的是,也有一些情况,编译器会很乐意接受推断类型,但由于多种原因之一,推断类型实际上无法显式写出。
例如,禁用单态限制将允许您的示例推断其类型(匹配
:t
所说的内容),尽管该类型需要手动编写的扩展。另一个例子是
where
中的定义。函数定义的子句,它使用父函数的多态参数。它们自己的类型不是多态的,由外部作用域中接收的参数决定,但父函数签名中的类型变量不在 where
的作用域内。条款¹。可能还有其他例子。¹ 来自父签名的类型变量可以通过
ScopedTypeVariables
进入作用域。扩展名和显式 forall
,如果需要的话。
关于haskell - GHCi 中的类型推断与手动签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8507288/