您不了解Hindley-Milner的哪一部分?


850

发誓曾经有一件不朽的字样出售的T恤


哪一部分

欣德利·米尔纳

明白吗


就我而言,答案将是……全部!

特别是,我经常在Haskell论文中看到这样的符号,但是我不知道它的含义。我不知道它应该是数学的哪个分支。

我当然知道希腊字母的字母和诸如“∉”之类的符号(通常表示某些东西不是集合的元素)。

另一方面,我以前从未见过“⊢”(维基百科声称这可能意味着“分区”)。我也不熟悉这里使用的vinculum。(通常情况下,它表示一个级分,但是这并不出现在这里是这种情况。)

如果有人至少可以告诉我从哪里开始寻找理解符号海的含义的方法,那将是有帮助的。


8
如果您正在寻找一种很好的算法解释,到目前为止,我发现的最好的就是Shriram Krishnamurthi的《编程语言:应用和解释》(CC许可!)的第30章。
laslowh

2
@laslowh谢谢!我在看 较新的版本:cs.brown.edu/courses/cs173/2012/book/book.pdf
SnowOnion,

Answers:


652
  • 水平杆意味着“[见上] 意味着 [下文]”。
  • 如果有多个表达式在[见上],则考虑它们相与在一起; 为了保证[以下],所有[以上]必须为真。
  • :表示有类型
  • 手段。(类似的意思是“不在”。)
  • Γ通常用于指环境或上下文;在这种情况下,可以将其视为一组类型注释,将标识符与其类型配对。因此x : σ ∈ Γ意味着环境Γ包括x具有类型的事实σ
  • 可以被证明证明Γ ⊢ x : σ表示环境Γ确定x具有类型σ
  • , 是一种方式 ,包括特定的附加假设到环境中Γ
    因此,Γ, x : τ ⊢ e : τ'意味着环境Γ与该附加,覆盖假定x有一个类型τ,证明了e具有类型τ'

根据要求:运算符优先级,从最高到最低:

  • 语言特定的缀和mixfix运营商,如λ x . e∀ α . σ,和τ → τ'let x = e0 in e1和空白为功能应用。
  • :
  • , (左联想)
  • 分隔多个命题的空格(关联)
  • 单杠

19
运营商的优先规则是什么?
2013年

:并且非常相似,因为它们意味着一件事是包含在另一件事情-一组包含元素和类型包含的值,在一定意义上。关键的区别在于,这x ∈ S意味着一个集合S从字面上包含一个元素x,而Γ ⊢ x : T意味着x可以推断出T上下文中的居住类型Γ。考虑到这一点,Var规则显示为:»如果x确实包含在上下文中,则可以(从中)推断出它«。
大卫,

@Randomblue我加括号随处可见,如作出了明确的符号的优先级(Γ,(x:τ))⊢(x:σ),见overleaf.com/read/ddmnkzjtnqbd#/61990222
SnowOnion

327

这种语法虽然看起来很复杂,但实际上相当简单。基本思想来自形式逻辑:整个表达式是一个含义,上半部分是假设,下半部分是结果。也就是说,如果您知道顶部表达式为真,则可以得出结论,底部表达式也为真。

符号

要记住的另一件事是,某些字母具有传统含义;特别是,Γ表示您所处的“上下文”,即您所看到的其他事物的类型。因此,类似的Γ ⊢ ...意思是“ ...当您知道中的每个表达式的类型时的表达式Γ

符号实质上意味着您可以证明某些东西。因此Γ ⊢ ...有句话说:“我可以...在上下文中证明Γ。这些声明也称为类型判断。

要记住的另一件事:在数学中,就像ML和Scala一样,x : σ意味着x具有type σ。您可以像Haskell的一样阅读它x :: σ

每个规则意味着什么

因此,知道了这一点,第一个表达式就变得容易理解:如果我们知道x : σ ∈ Γ(即在某种情况下x具有某种类型),那么我们就会知道(即在中具有type )。所以说真的,这并不是说任何有趣的事情。它只是告诉您如何使用上下文。σΓΓ ⊢ x : σΓxσ

其他规则也很简单。例如,以[App]。该规则有两个条件:e₀是从某种类型τ到某种类型的函数,τ'并且e₁是type 的值τ。现在你知道什么类型的,你将通过应用获得e₀e₁!希望这并不奇怪:)。

下一条规则具有更多新语法。特别地,Γ, x : τ正义指的是由上下文构成Γ和判断x : τ。因此,如果我们知道变量x的类型为τ,表达式e的类型为τ',则我们也知道接受x并返回的函数的类型e。这只是告诉我们如果我们确定了函数采用什么类型以及返回什么类型,该怎么办,因此也就不足为奇了。

下一个只是告诉您如何处理let语句。如果您知道某个表达式e₁具有与类型τ一样长x的类型σ,则let局部绑定x到type值的表达式σe₁具有一个type τ。确实,这只是告诉您,let语句实质上使您可以使用新的绑定来扩展上下文-正是这样let这样!

[Inst]规则处理子类型。它说,如果您有一个type值,σ'并且它是σ表示部分排序关系)的子类型,则该表达式也是 type σ

最终规则涉及泛化类型。简单来说:自由变量是一些表达式中的let语句或lambda不会引入的变量;该表达式现在,取决于其context.The规则的自由变量的值是说,如果有一些变量α不是 “免费”在您的上下文话,则可以安全地说,任何表达,它的类型你知道e : σ对于的任何值,将具有该类型α

如何使用规则

因此,既然您已经了解了符号,那么您将如何使用这些规则?好了,您可以使用这些规则来找出各种值的类型。为此,请查看您的表达式(例如f x y),然后找到一个结论与您的陈述相匹配的规则(底部)。让我们称您想要找到“目标”的东西。在这种情况下,您将查看以结尾的规则e₀ e₁。找到此规则后,现在必须查找证明该规则行以上所有内容的规则。这些东西通常与子表达式的类型相对应,因此您实际上是在表达式的某些部分上递归。您只需执行此操作,直到完成证明树,即可为您提供表达式类型的证明。

因此,所有这些规则所做的工作都是准确地指定-以及通常在数学上比较古怪的细节:P-如何确定表达式的类型。

现在,如果您曾经使用过Prolog,这听起来应该很熟悉-实际上,您就像在人工Prolog解释器中一样计算证明树。Prolog被称为“逻辑编程”的原因!这一点也很重要,因为向我介绍HM推理算法的第一种方法是在Prolog中实现它。这实际上是非常简单的,使事情变得清晰。您当然应该尝试一下。

注意:我可能在此解释中犯了一些错误,如果有人指出,我会喜欢的。实际上,我将在几周内在课堂上进行介绍,因此,我会比:P更自信。


5
\ alpha是非自由类型变量,不是常用变量。因此,要解释泛化规则,还需要解释更多。
nponeccop 2012年

2
@nponeccop:嗯,好点。我以前从未真正看到过该特定规则。你能帮我解释一下吗?
迪洪·杰维斯

8
@TikhonJelvis:这其实很简单,它可以让你概括(假设Γ = {x : τ}λy.x : σ → τ∀ σ. σ → τ,但不能∀ τ. σ → τ,因为τ是自由变量ΓHM上的Wikipedia文章对此进行了很好的解释。
Vitus 2012年

7
我认为答案的相关部分[Inst]有些不准确。到目前为止,这只是我的理解,但是[Inst][Gen]规则中的sigma 不是指类型,而是指类型方案。因此,运算符是与子类型无关的部分排序,正如我们从OO语言中了解到的那样。它与多态值有关,例如id = λx. x。此类功能的完整语法为id = ∀x. λx. x。现在,我们可以很明显有一个id2 = ∀xy. λx. x,在y不使用。然后id2 ⊑ id,这就是[Inst]规则说的话。
Ionuț G. Stan

71

如果有人至少可以告诉我从哪里开始寻找理解符号海的含义

有关通过判断和推导得出的逻辑样式,请参见“ 编程语言的实践基础 ”,第2章和第3章。整本书现在都可以在亚马逊上找到。

第2章

归纳定义

归纳定义是编程语言研究中必不可少的工具。在本章中,我们将开发归纳定义的基本框架,并给出一些使用它们的示例。归纳定义由一组规则组成,这些规则用于得出各种形式的判断断言。判断是关于指定类别的一个或多个句法对象的陈述。规则为判决的有效性规定了必要和充分的条件,从而充分确定了判决的含义。

2.1判决

我们从关于语法对象的判断断言的概念开始。我们将使用多种形式的判断,包括以下示例:

  • n natn是自然数
  • n = n1 + n2nn1n2的总和
  • τ - τ是一种类型
  • eτ—表达式e的类型为τ
  • ëv -表达Ë具有值v

一种判断表明一个或多个句法对象具有某种属性或彼此之间存在某种关系。属性或关系本身称为判断形式,而将一个或多个对象具有该属性或处于该关系中的判断称为该判断形式实例。判断形式也称为谓词,构成一个实例的对象是它的主语。我们写一个 Ĵ的判断断言Ĵ的持有一个。当强调判断的主题并不重要时,(此处剪掉文字)


53

我如何理解Hindley-Milner规则?

Hindley-Milner是顺序演算形式的一组规则(不是自然演绎)它表明我们可以在没有显式类型声明的情况下从程序的构造中推断出程序的(最一般的)类型。

符号和符号

首先,让我们解释符号,并讨论运算符优先级

  • 𝑥是标识符(非正式地,是变量名)。
  • 意思是一种类型(非正式地,是的一个实例或“是”)。
  • 𝜎(sigma)是一个变量或函数的表达式。
  • 因此𝑥:𝜎读为“ 𝑥is -a 𝜎
  • ∈表示“是...的元素”
  • 𝚪(伽玛)是一种环境。
  • (断言符号)表示断言(或证明,但在上下文中“断言”的理解更好。)
  • ⊦Γ 𝑥 σ被这样读出的“Γ断言𝑥,是- 一个σ
  • 𝑒是类型的实际实例(元件)σ
  • 𝜏(tau)是一种类型:基本,变量(𝛼),函数𝜏→𝜏'或乘积𝜏×𝜏'(此处未使用乘积)
  • 𝜏→𝜏'是一种功能类型,其中 𝜏𝜏'可能是不同的类型。
  • 𝜆𝑥.𝑒表示𝜆(lambda)是一个匿名函数,它接受参数𝑥并返回表达式𝑒

  • 𝑒₀中的𝑥 =𝑒₁ 表达式𝑒₁中表示𝑥,只要出现𝑒₀便substitute 代替

  • 表示前一个元素是后一个元素的子类型(非正式地为-子类)。

  • 𝛼是类型变量。
  • α.σ是一种类型的,∀(所有)参数变量, α,返回σ表达
  • ∉free (𝚪)表示不是在外部上下文中定义的the的自由类型变量的元素。(绑定变量是可替换的。)

线上方的一切都是前提,线以下的一切都是结论(PerMartin-Löf

优先,例如

我从规则中提取了一些更复杂的示例,并插入了显示优先级的多余括号:

  • 𝑥:σ ∈Γ可以写成(𝑥:σ) ∈Γ
  • Γ⊦ 𝑥 σ可以写成Γ⊦(𝑥 σ

  • Γ⊦ 𝑥 =𝑒₀ 𝑒₁τ 是等效Γ⊦((𝑥 =𝑒₀ 𝑒₁):τ

  • Γ⊦ λ𝑥.𝑒τ→τ '是等效Γ⊦((λ𝑥.𝑒):(τ→τ' ))

然后,将断言语句和其他先决条件分隔开的大空格表示一组这样的先决条件,最后,将前提与结论分隔开的水平线提出了优先顺序的结尾。

规则

接下来是对规则的英语解释,每条解释之后都进行了宽松的重述和解释。

变量

VAR逻辑图

给定𝑥是𝜎(sigma)的类型,是𝚪(Gamma)的元素,可以
得出结论𝚪断言𝑥是𝜎。

换句话说,在𝚪中,我们知道𝑥是𝑥类型,因为𝜎在𝜎中是of类型。

这基本上是重言式。标识符名称是变量或函数。

功能应用

APP逻辑图

给定𝚪断言𝑒₀是函数类型,并且𝚪断言a是𝜏
结论𝚪断言将函数𝑒₀应用于𝑒₁是类型𝜏'

为了重述该规则,我们知道函数应用程序返回类型𝜏',因为该函数具有类型𝜏→𝜏'并获得类型argument的参数。

这意味着,如果我们知道函数将返回类型,并将其应用于参数,则结果将是我们知道其返回的类型的实例。

功能抽象

ABS逻辑图

给定类型的𝑥和s断言𝑒是一种类型,conclude'
结论conclude断言一个匿名函数,𝜆的𝑥返回表达式,𝑒的类型为𝜏→𝜏'。

同样,当我们看到一个接受𝑥并返回表达式𝑒的函数时,我们知道它的类型为𝜏→𝜏',因为𝑥(a 𝜏)断言𝑒是𝜏'。

如果我们知道𝑥是type类型,因此表达式𝑒是𝜏'类型,那么𝑥返回表达式𝑒的函数就是𝜏→𝑒'类型。

让变量声明

LET逻辑图

给定Γ断言𝑒₀,类型σ的, Γ和𝑥,型σ的断言类型τ的𝑒₁
结束Γ断言let𝑥=𝑒₀ in类型τ的𝑒₁

松散地,because是在𝑒₁(a 𝜏)中绑定到𝑒₀的,因为𝑒₀是𝜎,并且𝑥是断言𝑒₁是𝜏的𝜎。

这意味着,如果我们有一个表达式𝑒₀是𝑒₀(是变量或函数),并且有一个名称𝑥,也是also,并且表达式𝑒₁的类型为then,那么我们可以用𝑒₀代替inside出现在里面𝑒₁。

实例化

INST逻辑图

给定𝚪断言𝜎'的类型,并且𝜎'是𝜎的子类型。
结论𝚪断言𝑒的类型为𝜎

表达式𝑒是父类型𝜎,因为表达式𝑒是子类型𝜎',而𝜎是𝜎'的父类型。

如果实例的类型是其他类型的子类型,则它也是该超类型的实例-更通用的类型。

概括

GEN逻辑图

给定𝚪断言𝑒是𝜎, 𝛼不是the的自由变量的元素,则
得出结论𝚪断言𝑒,为所有自变量表达式type返回𝜎表达式

因此,一般来说,对于所有返回𝜎的参数变量(𝛼),都将𝑒键入𝜎,因为我们知道𝑒是𝜎,并且𝛼不是自由变量。

这意味着我们可以泛化程序以接受所有类型的参数,这些参数尚未包含在包含范围内(非局部变量)。这些绑定变量是可替换的。

全部放在一起

给定某些假设(例如,没有自由/未定义的变量,已知的环境),我们知道以下类型:

  • 我们程序的原子元素(变量),
  • 函数(函数应用程序)返回的值,
  • 功能构造(功能抽象),
  • 让绑定(让变量声明),
  • 实例的父类型(实例化),以及
  • 所有表达式(通用化)。

结论

这些规则的结合使我们能够证明断言程序的最通用类型,而无需类型注释。


1
亚伦这么好回顾!
bhurlow

48

该符号来自自然推论

⊢符号称为旋转门

这六个规则非常简单。

Var 规则是相当琐碎的规则-它说如果标识符的类型已经存在于您的类型环境中,那么就可以直接从环境中推断出该类型。

App规则说,如果您有两个标识符e0e1并且可以推断出它们的类型,那么就可以推断出application的类型e0 e1。如果您知道e0 :: t0 -> t1e1 :: t0(相同的t0!),则规则将如下所示,则应用程序的类型正确,类型为t1

Abs并且Let是推断lambda抽象和让入类型的规则。

Inst 规则说,您可以用不太通用的类型替换类型。


4
这是后来的演算,不是自然推论。
Roman Cheplyaka 2012年

12
@RomanCheplyaka好,表示法几乎相同。维基百科文章对这两种技术进行了有趣的比较:en.wikipedia.org/wiki/Natural_deduction#Sequent_calculus。后来的演算是对自然推论的失败的直接反应而产生的,因此,如果问题是“这个符号来自哪里”,那么“自然推论”在技术上是一个更正确的答案。
丹·伯顿

2
@RomanCheplyaka另一个考虑因素是,顺序演算纯粹是语法上的(这就是为什么有这么多结构规则的原因),而这种表示法则没有。第一条规则假设上下文是一个集合,而在后续演算中,它是一个更简单的语法构造。
nponeccop 2012年

@Cheplyaka实际上,不,它看起来像“后续”,但不是后续演算。哈珀在他的教科书中将此理解为“高阶判断”。这确实是自然的推论。
菲利普·JF

15

有两种方法可以考虑e:σ。一个是“表达式e具有类型σ”,另一个是“表达式e和类型σ的有序对”。

将Γ看作是有关表达式类型的知识,实现为一组表达式和类型对,即e:σ。

旋转门⊢意味着从左边的知识,我们可以推断出右边的东西。

因此,可以读取第一个规则[Var]:
如果我们的知识Γ中包含对e:σ,那么我们可以从Γ中推论出e的类型为σ。

可以理解为第二条规则[App]:
如果从Γ可以推断出e_0的类型为τ→τ',从Γ可以推断出e_1的类型为τ,那么从Γ可以推断出e_0的e_1具有类型τ。输入τ'。

通常写Γ,e:σ而不是Γ∪{e:σ}。

因此可以
得出第三条规则[Abs]:如果从Γ扩展了x:τ可以推论出e的类型为τ',那么从Γ可以推论出λx.e的类型为τ→τ'。

保留第四条规则[Let]作为练习。:-)

可以读出第五条规则[Inst]:
如果从Γ中我们可以推断出e具有σ'类型,而σ'是σ的子类型,那么从Γ中我们可以推断出e具有σ'类型。

可以阅读第六条也是最后一条规则[Gen]:
如果我们从Γ中可以推断出e具有类型σ,而在任何Γ类型中α都不是自由类型变量,那么从Γ中我们可以推断出e具有类型∀ασ。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.