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

Haskell GHCi中的类型推断与手动签名,haskell,type-inference,ghc,ghci,Haskell,Type Inference,Ghc,Ghci,我打字的时候 :t map length . sum 在GHCi中,它表示类型为: map length . sum :: Num [[a]] => [[[a]]] -> [Int] 但是,如果我创建一个包含 x :: Num [[a]] => [[[a]]] -> [Int] x = map length . sum ghc和ghci都抱怨: type-test.hs:1:1: Non type-variable argument in the const

我打字的时候

:t map length . sum
在GHCi中,它表示类型为:

map length . sum :: Num [[a]] => [[[a]]] -> [Int]
但是,如果我创建一个包含

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]
>设x=贴图长度。总和::(Num[[a]])=>[[a]]]->[Int]
:0:9:
约束中的非类型变量参数:Num[[a]]
(使用-XFlexibleContexts允许此操作)
在表达式类型签名中: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
>设x=贴图长度。总和
:0:22:
没有(Num[[a0]]的实例)
因使用“sum”而产生
可能的修复方法:添加(Num[[a0]]的实例声明
在“()”的第二个参数中,即“sum”
在表达式中:映射长度。总和
这与加载包含没有类型签名的定义的文件时产生的错误大致相同

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

GHCi中的
:t
命令不是编译过程的一部分——它是类型检查和推理系统的热线,让您询问假设代码的类型。当一个更通用的类型仍然可以提供信息时,它没有明显的理由基于扩展来任意限制自己,原因与上面的错误消息告诉您使用-XFlexibleContexts来允许这个,而不仅仅是类型约束中的语法错误


更有趣的是,也有一些情况下,编译器会愉快地接受推断类型,但由于以下原因之一,推断类型实际上无法显式写出

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

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

如果需要,可以使用
ScopedTypeVariables
扩展和一个显式的
for all
,将父签名中的类型变量纳入范围。

(这并没有回答您原来的问题,而是解决了代码的问题)

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

map length . sum
意思是“取我的数字列表,求和,然后计算结果数字中每个元素的长度(??)”,这没有意义

你可能是说:

sum . map length
这意味着“取我的列表,计算每个元素的长度,并求和长度。”


错误消息本身的意思是,由于
sum
返回一个数字,也就是
sum
的类型是
Num n=>[n]->n
,然后您尝试对该类型使用
map length
,它的类型为
Num m=>[[a]->[m]
,编译器尝试说
n=[[a]]
,以使类型匹配,问题是这个函数是完全可以接受的,但是语言语法不允许指定它最通用的类型。在这种情况下,
ghci
可以做什么?我只能想到两个选项,要么给出只能在启用某些扩展的情况下指定的类型,要么给出一个错误。对我来说,给出一个错误而不提及它可以通过启用扩展来修复似乎不是很理想。只要让
:t
报告推断出的最通用类型就更简单了,而且很可能在实现该功能时,没有人想到会出现这种情况。

另一种看待它的方式:

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


这就像有些女孩告诉你她会在地狱结束后和你约会。你不会抱怨她答应和我一起出去,尽管这个世界上没有任何可能,你会抱怨吗?你可能会注意到她只是或多或少地礼貌地说了“不”。

但是请注意,
ghci>let x=map length。如果禁用单态限制,sum
可以工作,
ghci>let x xs=map length(sum xs)
也可以用于MR。第一个ghci错误只是因为试图根据默认规则对类型进行单态化。@Danielfisher:是的,我一发布就意识到我应该澄清这一点,呵呵。我认为,关于不可写类型的问题是一个有趣的补充。我不确定类型默认在这里有什么特别的意义…?“[[a]]”可以通过许多不同的有用方式成为Num的一个实例。例如,[[Int]]可能是一个(不安全的)整数矩阵,因此地狱冻结在争论之上。吹毛求疵的西里尔语教授:请注意,拥有
Num[[Int]]
并不意味着拥有
Num[[a]]