我正在阅读关于Hindley–Milner Type Inference的维基百科文章,试图从中找出一些意义。到目前为止,这是我所理解的:
int
或string
)或类型变量(如α
和β
)。int
和string
),也可以是类型构造函数(如Map
和Set
)。α
和β
)表现为具体类型的占位符(例如int
和string
)。现在我在理解多类型方面遇到了一些困难,但在学习了一些Haskell后,我就是这样做了:
int
和string
)和类型变量(如α
和β
)属于*
。Map
和Set
)是类型的lambda抽象(例如Set
属于* -> *
种类,Map
属于* -> * -> *
种类∀α.σ
1}})。我不明白的是限定词的含义。例如{{1}}代表什么?我似乎无法做出它的正面或反面,我读的越多,我得到的就越混乱:
具有多型 ∀α.α - >的函数。相比之下,α 可以将相同类型的任何值映射到自身,identity function是此类型的值。作为另一个例子 ∀α。(Setα) - > int 是将所有有限集映射到整数的函数的类型。成员数是此类型的值。请注意,限定符只能显示为顶级,即类型 ∀α.α - >例如,∀α.α 被类型的语法排除,并且单型包含在多型中,因此类型的一般形式为 ∀α1。 。 。 ∀αₙ.τ
答案 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
等。如果没有这种隐式的通用量化,类型变量b
和map
必须具有一些固定的含义,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
。