我如何理解Hindley-Milner规则?
Hindley-Milner是顺序演算形式的一组规则(不是自然演绎)它表明我们可以在没有显式类型声明的情况下从程序的构造中推断出程序的(最一般的)类型。
符号和符号
首先,让我们解释符号,并讨论运算符优先级
- 𝑥是标识符(非正式地,是变量名)。
- :意思是一种类型(非正式地,是的一个实例或“是”)。
- 𝜎(sigma)是一个变量或函数的表达式。
- 因此𝑥:𝜎读为“ 𝑥is -a 𝜎 ”
- ∈表示“是...的元素”
- 𝚪(伽玛)是一种环境。
- ⊦(断言符号)表示断言(或证明,但在上下文中“断言”的理解更好。)
- ⊦Γ 𝑥 : σ被这样读出的“Γ断言𝑥,是- 一个σ ”
- 𝑒是类型的实际实例(元件)σ。
- 𝜏(tau)是一种类型:基本,变量(𝛼),函数𝜏→𝜏'或乘积𝜏×𝜏'(此处未使用乘积)
- 𝜏→𝜏'是一种功能类型,其中 𝜏和𝜏'可能是不同的类型。
𝜆𝑥.𝑒表示𝜆(lambda)是一个匿名函数,它接受参数𝑥并返回表达式𝑒。
令 𝑒₀中的𝑥 =𝑒₁ 在 表达式𝑒₁中表示𝑥,只要出现𝑒₀便用substitute 代替。
⊑表示前一个元素是后一个元素的子类型(非正式地为-子类)。
- 𝛼是类型变量。
- ∀ α.σ是一种类型的,∀(所有)参数变量, α,返回σ表达
- ∉free (𝚪)表示不是在外部上下文中定义的the的自由类型变量的元素。(绑定变量是可替换的。)
线上方的一切都是前提,线以下的一切都是结论(PerMartin-Löf)
优先,例如
我从规则中提取了一些更复杂的示例,并插入了显示优先级的多余括号:
然后,将断言语句和其他先决条件分隔开的大空格表示一组这样的先决条件,最后,将前提与结论分隔开的水平线提出了优先顺序的结尾。
规则
接下来是对规则的英语解释,每条解释之后都进行了宽松的重述和解释。
变量
给定𝑥是𝜎(sigma)的类型,是𝚪(Gamma)的元素,可以
得出结论𝚪断言𝑥是𝜎。
换句话说,在𝚪中,我们知道𝑥是𝑥类型,因为𝜎在𝜎中是of类型。
这基本上是重言式。标识符名称是变量或函数。
功能应用
给定𝚪断言𝑒₀是函数类型,并且𝚪断言a是𝜏
结论𝚪断言将函数𝑒₀应用于𝑒₁是类型𝜏'
为了重述该规则,我们知道函数应用程序返回类型𝜏',因为该函数具有类型𝜏→𝜏'并获得类型argument的参数。
这意味着,如果我们知道函数将返回类型,并将其应用于参数,则结果将是我们知道其返回的类型的实例。
功能抽象
给定类型的𝑥和s断言𝑒是一种类型,conclude'
结论conclude断言一个匿名函数,𝜆的𝑥返回表达式,𝑒的类型为𝜏→𝜏'。
同样,当我们看到一个接受𝑥并返回表达式𝑒的函数时,我们知道它的类型为𝜏→𝜏',因为𝑥(a 𝜏)断言𝑒是𝜏'。
如果我们知道𝑥是type类型,因此表达式𝑒是𝜏'类型,那么𝑥返回表达式𝑒的函数就是𝜏→𝑒'类型。
让变量声明
给定Γ断言𝑒₀,类型σ的,和 Γ和𝑥,型σ的断言类型τ的𝑒₁
结束Γ断言let
𝑥=𝑒₀ in
类型τ的𝑒₁
松散地,because是在𝑒₁(a 𝜏)中绑定到𝑒₀的,因为𝑒₀是𝜎,并且𝑥是断言𝑒₁是𝜏的𝜎。
这意味着,如果我们有一个表达式𝑒₀是𝑒₀(是变量或函数),并且有一个名称𝑥,也是also,并且表达式𝑒₁的类型为then,那么我们可以用𝑒₀代替inside出现在里面𝑒₁。
实例化
给定𝚪断言𝜎'的类型,并且𝜎'是𝜎的子类型。
结论𝚪断言𝑒的类型为𝜎
表达式𝑒是父类型𝜎,因为表达式𝑒是子类型𝜎',而𝜎是𝜎'的父类型。
如果实例的类型是其他类型的子类型,则它也是该超类型的实例-更通用的类型。
概括
给定𝚪断言𝑒是𝜎,而 𝛼不是the的自由变量的元素,则
得出结论𝚪断言𝑒,为所有自变量表达式type返回𝜎表达式
因此,一般来说,对于所有返回𝜎的参数变量(𝛼),都将𝑒键入𝜎,因为我们知道𝑒是𝜎,并且𝛼不是自由变量。
这意味着我们可以泛化程序以接受所有类型的参数,这些参数尚未包含在包含范围内(非局部变量)。这些绑定变量是可替换的。
全部放在一起
给定某些假设(例如,没有自由/未定义的变量,已知的环境),我们知道以下类型:
- 我们程序的原子元素(变量),
- 函数(函数应用程序)返回的值,
- 功能构造(功能抽象),
- 让绑定(让变量声明),
- 实例的父类型(实例化),以及
- 所有表达式(通用化)。
结论
这些规则的结合使我们能够证明断言程序的最通用类型,而无需类型注释。