为什么3和x(被分配了3)在Haskell中有不同的推断types?
Haskell中的types推理有一点学习曲线(至less可以说!)。 开始学习的一个好方法是用简单的例子。 所以,以下是一个types推理的“hello world”。
考虑下面的例子:
Prelude> :t 3 3 :: (Num t) => t Prelude> let x = 3 Prelude> :tx x :: Integer
问题是: 为什么3和x有不同的types?
链接总结:
阅读下面的答案全文; 这里只是一个链接总结:
- GHCtypes违约: Haskell报告部分4.3.4
- GHCi的扩展types违约: 使用GHCi部分2.4.5
- 单态限制: Haskell wiki
这里还有另一个因素,在acfoltzer包含的一些链接中提到,但在这里可能值得一提。 你遇到单态限制的效果。 当你说
let x = 5
你做一个variables的顶层定义。 MR坚持认为,这种定义在没有types签名的情况下,应该通过为未解决的typesvariablesselect(希望)合适的缺省实例来专门化为单态值。 相比之下,当你使用:t
来要求一个推断的types时,不会有这样的限制或违约。 所以
> :t 3 3 :: (Num t) => t
因为3
确实被重载:它被任何数字types所承认。 默认规则selectInteger
作为默认的数字types,所以
> let x = 3 > :tx x :: Integer
但现在让我们关掉MR。
> :set -XNoMonomorphismRestriction > let y = 3 > :ty y :: (Num t) => t
没有MR,定义就像多态一样,就像3
重载一样。 只是检查…
> :ty * (2.5 :: Float) y * (2.5 :: Float) :: Float > :ty * (3 :: Int) y * (3 :: Int) :: Int
请注意,根据与相关Num
实例一起提供的fromInteger
方法,多态y = 3
在这些用途中具有不同的专用性。 也就是说, y
不与3
的特定表示相关联,而是用于构造3
表示的scheme。 早就编译,这是一个缓慢的食谱,有人引用为MR的动机。
我(在当地假装是)中立的辩论是否单形限制是一个较小或更大的邪恶。 我总是为顶层定义编写types签名,所以我想要实现什么,而MR是不重要的。
当试图学习types系统如何工作时,分离types推断的方面是非常有用的
-
“遵循计划”,将多态定义专门化为特定的用例:一个相当强大的约束解决问题,需要通过反向链接进行基本的统一和实例parsing; 和
-
“猜测计划”,将types泛化为一个没有types签名的定义的多态typesscheme:这是非常脆弱的,你越移越基本的Hindley-Milner学科,带有types类,更高级的多态性, GADT,陌生的东西变成了。
学习第一个如何工作,并理解为什么第二个是困难的,这是很好的。 许多types推断中的怪异与第二种相关,并且启发式,如单形性限制试图在歧义面前提供有用的默认行为。
这是因为GHCi中的types违约而发生的,正如这里 在 这里和这里以及其他中所讨论的那样。 不幸的是,这似乎是一个难以search的东西,因为在知道短语“types违约”之前,有很多方法可以描述这种行为。
更新 :哦。 去掉了不好的例子。
由于没有人提到过为什么会有单态限制,所以我想我会joinHaskell的历史:与Class一起懒惰 。
6.2单态限制早期争论的一个主要来源是所谓的“单态限制”。假设genericLength有这个重载types:
genericLength :: Num a => [b] -> a
现在考虑这个定义:
f xs = (len, len) where len = genericLength xs
看起来
len
应该只计算一次,但实际上可以计算两次。 为什么? 因为我们可以推导出typeslen :: (Num a) => a
; 当通过字典传递转换parsing时,len
变成一个函数,在每个len
出现时被调用一次,每个len
可能以不同的types使用。休斯坚决主张,以这种方式默默地重复计算是不可接受的。 他的论点是由他写的一个程序所驱动的,其速度比他预期的要慢得多。 (这当然是用一个非常简单的编译器,但是我们不愿意使性能差异像编译器优化一样大。)
经过多次辩论,委员会采取了现在臭名昭着的单形制约。 简单地说,它表示一个看起来不像函数的定义(即在左边没有参数)在任何重载的typesvariables中应该是单态的。 在这个例子中,规则强制
len
在其出现时使用相同的types,这解决了性能问题。 如果需要多态行为,程序员可以为len
提供显式的types签名。单态的限制显然是语言上的一个瑕疵。 它似乎咬了每一个新的Haskell程序员引起一个意想不到的或模糊的错误信息。 已经有很多的替代scheme的讨论。 格拉斯哥Haskell编译器(GHC,第9.1节)提供了一个标签:
-fno-monomorphism-restriction
完全压制这个限制。 但在这个时候,没有真正令人满意的select发展。
我发现这篇论文对单态限制的语气非常有趣。