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

发布于 2024-12-21 08:09:27 字数 634 浏览 6 评论 0原文

当我输入

: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 ,则会抱怨:

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]

Why does ghci allowed me to infer the type当FlexibleContexts未启用时,这个(使用:t)?

when I type

:t map length . sum

into GHCi, it says that the type would be:

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

However, if I create a file type-test.hs containing

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

both ghc and ghci complain:

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]

Why does ghci allow me to infer the type for this (using :t), when FlexibleContexts are not enabled?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

稀香 2024-12-28 08:09:27

首先,我们要澄清一件事。如果我们在 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 命令不是编译过程的一部分——它是类型检查和推理系统的热线,让您询问假设代码的类型。当更通用的类型仍然可以提供信息时,没有明显的理由让它根据扩展任意限制自身,出于同样的原因,上面的错误消息告诉您使用 -XFlexibleContexts 来允许这样做而不是只是类型约束中的语法错误


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

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

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

1 如果需要,可以使用 ScopedTypeVariables 扩展和显式的 forall 将来自父级签名的类型变量引入作用域。

First, let's clarify one thing. What happens if we define the function in GHCi, rather than querying the type?

> 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]

And so on. In other words, the same thing. What if we let GHCi infer the type of the definition?

> 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

This is roughly the same error that results from loading a file containing the definition without a type signature.

What's the upshot of all this? Well, think about the fact that it tells you what extension is needed. GHC is capable of recognizing what the type means, even if it rejects the type by default. I'd hardly expect GHC to use a completely different type checker depending on the combination of extensions used, so it seems easy to conclude that the offending type is being rejected for no reason other than the relevant extension being disabled.

The :t command in GHCi isn't part of the compilation process--it's a hotline to the type checking and inference system, letting you ask the type of hypothetical code. There's no obvious reason for it to restrict itself arbitrarily based on extensions, when a more general type could still be informative, for the same reason that the error messages above tell you use -XFlexibleContexts to permit this rather than merely syntax error in type constraint.


As a possibly more interesting aside, there are also cases where an inferred type will be accepted happily by the compiler, but the inferred type can't actually be written out explicitly for one of several reasons.

For instance, disabling the monomorphism restriction will allow your example to have its type inferred (matching what :t says), despite that type requiring an extension to write manually.

Another example are definitions in the where clause of a function definition which make use of polymorphic arguments to the parent function. Their own types are not polymorphic, being dictated by the arguments received in the outer scope, yet the type variables in the parent function's signature are not in scope for the where clause¹. There may be other examples as well.

¹ The type variables from the parent's signature can be brought into scope with the ScopedTypeVariables extension and an explicit forall, if that's needed.

慈悲佛祖 2024-12-28 08:09:27

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

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

map length . sum

意思是“获取我的数字列表,并将其求和,然后计算结果数字的每个元素(??)的长度。”这没有任何意义。

您可能的意思是:

sum . map length

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

错误消息本身的含义是,由于 sum 返回一个数字,因此 sum 的类型为 Num n => [n]-> n,然后尝试在其上使用 map length,其类型为 Num m =>; [[a]]-> [m],编译器试图说 n == [[a]] 以使类型匹配,然后一切都从那里开始走下坡路。

(This doesn't answer your original question, but solves the problem with the code instead)

These errors hint at that your code as written probably isn't what you meant. This code:

map length . sum

means "Take my list of numbers, and sum it, then compute the length of each element(??) of the resulting number." That makes no sense.

You probably meant:

sum . map length

which means "Take my list of lists, compute the length of every element, and sum the lengths."

What the error message itself means is that since sum returns a number, aka the type of sum is Num n => [n] -> n, and you then try to use map length on that, which has type Num m => [[a]] -> [m], the compiler tries to say that n == [[a]] to make the types match, and everything goes downhill from there.

執念 2024-12-28 08:09:27

另一种看待它的方式:

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

这就像有些女孩告诉你,当地狱结冰时,她会和你出去。你不会抱怨她答应和我出去,尽管这个世界上不存在地狱会冻结的可能性,不是吗?您宁愿注意到她只是以或多或少礼貌的方式说

Another way to look at it:

Haskell tells you that your expression had a valid type, namely [[[a]]] -> [Int] if only [[a]] would be an instance of Num.

This is like some girls telling you she will go out with you when hell freezes over. You wouldn't complain that she promised to go out with me despite the hell can freeze over possibility is not given in this world, would you? You would rather notice that she just said No in a more or less polite way.

冬天旳寂寞 2024-12-28 08:09:27

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

The problem is that the function is perfectly acceptable, but its most general type is not allowed to be specified by the language grammar. What could ghci do in such circumstances? I can only think of two options, either give a type that can only be specified with some extension enabled, or giving an error. Giving an error without mentioning that it can be fixed by enabling an extension seems not very desirable to me. Just having :t report the inferred most general type is simpler, and quite possibly when the feature was implemented, nobody thought of situations like this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文