理解Hindley-Milner型推理中的多义性

时间:2013-03-19 10:47:11

标签: haskell type-inference type-systems lambda-calculus hindley-milner

我正在阅读关于Hindley–Milner Type Inference的维基百科文章,试图从中找出一些意义。到目前为止,这是我所理解的:

  1. 类型分为单型或多型。
  2. Monotypes进一步分类为类型常量(如intstring)或类型变量(如αβ)。
  3. 类型常量可以是具体类型(如intstring),也可以是类型构造函数(如MapSet)。
  4. 类型变量(如αβ)表现为具体类型的占位符(例如intstring)。
  5. 现在我在理解多类型方面遇到了一些困难,但在学习了一些Haskell后,我就是这样做了:

    1. 类型本身有类型。正式类型的类型称为种类(即,有不同种类的类型)。
    2. 具体类型(如intstring)和类型变量(如αβ)属于*
    3. 类型构造函数(如MapSet)是类型的lambda抽象(例如Set属于* -> *种类,Map属于* -> * -> *种类∀α.σ 1}})。
    4. 我不明白的是限定词的含义。例如{{1}}代表什么?我似乎无法做出它的正面或反面,我读的越多,我得到的就越混乱:

        

      具有多型 ∀α.α - >的函数。相比之下,α 可以将相同类型的任何值映射到自身,identity function是此类型的值。作为另一个例子 ∀α。(Setα) - > int 是将所有有限集映射到整数的函数的类型。成员数是此类型的值。请注意,限定符只能显示为顶级,即类型 ∀α.α - >例如,∀α.α 被类型的语法排除,并且单型包含在多型中,因此类型的一般形式为 ∀α1。 。 。 ∀αₙ.τ

2 个答案:

答案 0 :(得分:19)

首先,种类和多态类型是不同的东西。你可以有一个HM类型的系统,其中所有类型都是同一类型(*),你也可以有一个没有多态但系统复杂的系统。

如果字词M的类型为∀a.t,则表示对于s类型,s代替a t t[a:=s] (通常写为M,我们将t[a:=s]的类型forall。这有点类似于逻辑,我们可以用任何术语替换普遍量化的变量,但是我们在这里处理类型。

这正是Haskell中发生的事情,只是在Haskell中你没有看到量词。显示在类型签名中的所有类型变量都是隐式量化的,就像在类型前面有map一样。例如,map :: forall a . forall b . (a -> b) -> [a] -> [b] 将具有类型

a

等。如果没有这种隐式的通用量化,类型变量bmap必须具有一些固定的含义,let不会是多态的。

HM算法区分类型(没有量词,单型)和类型模式(通用量化类型,多类型)。重要的是,在某些地方它使用类型模式(如forall),但在其他地方只允许使用类型。这使得整个事情可以判定。

我还建议您阅读有关System F的文章。它是一个更复杂的系统,它允许forall在类型中的任何地方(因此它的所有内容都称为 type ),但类型推断/检查是不可判定的。它可以帮助您了解{{1}}的工作原理。系统F在Girard,Lafont和Taylor中有详细描述,Proofs and Types

答案 1 :(得分:4)

在Haskell中考虑l = \x -> t。它是一个lambda,它表示一个变量t的术语x,稍后将被替换(例如l 1,无论它意味着什么)。同样,∀α.σ表示类型变量为α的类型,即f : ∀α.σ,如果函数由类型α参数化。在某种意义上,σ取决于α,因此f会返回σ(α)类型的值,其中α将在σ(α)中被替换,我们将得到一些具体的类型。

在Haskell中,您可以省略并定义函数,就像id : a -> a一样。允许省略量词的原因基本上是因为它们只允许顶级(没有RankNTypes扩展名)。你可以尝试这段代码:

id2 : a -> a -- I named it id2 since id is already defined in Prelude
id2 x = x

如果您向ghci询问id:t id)的类型,则会返回a -> a。为了更精确(更多类型理论),id具有类型∀a. a -> a。现在,如果您添加到您的代码:

val = id2 3

,3的类型为Int,因此类型Int将替换为σ,我们将获得具体类型Int -> Int