为什么3和x(已分配3)在Haskell中具有不同的推断类型?

Pet*_*rko 26 haskell type-inference monomorphism-restriction

Haskell中的类型推断有一点学习曲线(至少可以说!).开始学习它的一个好方法是使用简单的例子.因此,以下是类型推断的"hello world".

请考虑以下示例:

Prelude> :t 3
3 :: (Num t) => t
Prelude> let x = 3
Prelude> :t x
x :: Integer
Run Code Online (Sandbox Code Playgroud)

问题是:为什么3和x有不同的类型?

链接摘要:

阅读以下答案的完整故事; 这里只是一个链接摘要:

  1. GHC类型默认:Haskell报告第4.3.4节
  2. GHCi的扩展类型默认值:使用GHCi第2.4.5节
  3. 单形限制:Haskell维基

pig*_*ker 32

这里有另一个因素,在acfoltzer包含的一些链接中提到过,但在这里可能值得明确.你遇到了单态限制的影响.当你说

let x = 5
Run Code Online (Sandbox Code Playgroud)

你做一个变量顶级定义.MR坚持认为,这些定义在没有类型签名的情况下,应该通过为未解析的类型变量选择(希望)合适的默认实例来专用于单形值.相反,当您使用要求推断类型时,不会施加此类限制或违约.所以:t

> :t 3
3 :: (Num t) => t
Run Code Online (Sandbox Code Playgroud)

因为3确实超载了:它被任何数字类型所接受.默认规则选择Integer默认数字类型,因此

> let x = 3
> :t x
x :: Integer
Run Code Online (Sandbox Code Playgroud)

但现在让我们关掉MR.

> :set -XNoMonomorphismRestriction
> let y = 3
> :t y
y :: (Num t) => t
Run Code Online (Sandbox Code Playgroud)

没有MR,定义就像它可以是多态的一样,就像重载一样3.只是检查...

> :t y * (2.5 :: Float)
y * (2.5 :: Float) :: Float
> :t y * (3 :: Int)
y * (3 :: Int) :: Int
Run Code Online (Sandbox Code Playgroud)

请注意y = 3,根据fromInteger相关Num实例提供的方法,多态在这些用途中具有不同的专业性.也就是说,y不与特定表示相关联3,而是与构建表示的方案相关联3.天真的编译,这是一个缓慢的配方,有些人认为这是MR的动机.

我(当地假装)在关于单态限制是否是一个更小或更大的邪恶的辩论中保持中立.我总是为顶级定义编写类型签名,因此对于我想要实现的内容没有任何歧义,并且MR不是重点.

在尝试学习类型系统如何工作时,将类型推断的各个方面分开是非常有用的

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

  2. "猜测计划",推广类型以将多态类型方案分配给没有类型签名的定义:这非常脆弱,你越过基本的Hindley-Milner规则,类型类,具有更高级别的多态性, GADTs,陌生人的事情变成了.

很好地了解第一个如何工作,并理解为什么第二个是困难的.类型推断的许多奇怪之处与第二种相关,并且具有启发性,例如单态性限制试图在模糊性面前提供有用的默认行为.

  • 无论你对编译程序的MR有什么看法,每个人似乎都同意它在GHCi提示符下默认不应该生效,如本例所示.[GHC bug#3202](http://hackage.haskell.org/trac/ghc/ticket/3202)解决了这个问题.它被安排在7.2.1刚刚发布,但看起来它没有进入.希望它将在下一版本的GHC中修复. (3认同)
  • @Yitz那确实是个好结果.我倾向于说默认情况下MR应该关闭,但是收集有关在现有代码库中引入了多少类型/性能问题的硬数据会很好.我也想知道GHC的专业化机制是否可以让我们双管齐下.但我推测. (2认同)

acf*_*zer 6

这是因为GHCi中类型默认,如此,此处,此处以及此处所讨论的.不幸的是,这似乎很难搜索,因为在你知道短语"type defaulting"之前有很多方法可以描述这种行为.

更新:D'哦.删除了糟糕的例子.

  • Ehrm,这个答案非常具有误导性."x"的类型为"Num a => a"是完全没问题的.事实上,如果你禁用单态限制或明确给出`x`这种类型,它将具有该类型.在那种情况下,做"x*3.0"并没有什么不妥.对于数字类型的默认值也不是特定于ghci的. (6认同)