为什么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?

链接总结:

阅读下面的答案全文; 这里只是一个链接总结:

  1. GHCtypes违约: Haskell报告部分4.3.4
  2. GHCi的扩展types违约: 使用GHCi部分2.4.5
  3. 单态限制: 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推断的方面是非常有用的

  1. “遵循计划”,将多态定义专门化为特定的用例:一个相当强大的约束解决问题,需要通过反向链接进行基本的统一和实例parsing; 和

  2. “猜测计划”,将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发展。

我发现这篇论文对单态限制的语气非常有趣。

Interesting Posts