关于哈斯克尔:“你不明白欣德利 – 米尔纳的哪一部分?”

“What part of Hindley-Milner do you not understand?”

我发誓曾经有一件T恤出售,上面写着不朽的话:

什么部分

Hindley-Milner

难道你不明白?

就我而言,答案就是......全部!

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

我当然认识到希腊字母的字母,以及诸如"?"之类的符号。 (这通常意味着某些东西不是一组的元素)。

另一方面,我从未见过"?" 之前(维基百科称它可能意味着"分区")。 我也不熟悉这里使用的vinculum。 (通常它表示一个分数,但这似乎不是这种情况。)

如果有人至少可以告诉我从哪里开始想要理解这个符号海洋的含义,那将会有所帮助。


  • 水平条表示"[以上]暗示[下方]"。
  • 如果[上面]中有多个表达式,那么将它们一起考虑;所有[上述]必须为真以保证[下方]。
  • :表示有类型
  • 表示在。(同样?表示"不在"中。)
  • Γ通常用于指代环境或上下文;在这种情况下,它可以被认为是一组类型注释,将标识符与其类型配对。因此,x : σ ∈ Γ表示环境Γ包括x具有类型σ的事实。
  • ?可以作为证明或确定来阅读。 Γ ? x : σ表示环境Γ确定x的类型为σ
  • ,是一种将特定的附加假设包含在环境Γ中的方法。
    因此,Γ, x : τ ? e : τ'表示环境Γ,其中x具有类型τ的附加的重写假设证明e具有类型τ'

根据要求:运营商优先级,从最高到最低:

  • 特定于语言的中缀和mixfix运算符,例如λ x . e? α . στ → τ'let x = e0 in e1以及用于函数应用的空白。
  • :
  • ?
  • ,(左联想)
  • ?
  • 分隔多个命题的空格(关联)
  • 横杠


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

符号

另外要记住的是,有些字母具有传统意义;特别是,Γ代表你所处的"背景" - 即你所看到的其他类型的东西。所以当你知道Γ中每个表达式的类型时,像Γ ? ...这样的意思是"表达式...

?符号实质上意味着您可以证明某些东西。所以Γ ? ...是一个声明,说"我可以在上下文Γ中证明...。这些语句也称为类型判断。

另外要记住的是:在数学中,就像ML和Scala一样,x : σ意味着x的类型为σ。您可以像Haskell的x :: σ一样阅读它。

每条规则的含义

所以,知道这一点,第一个表达式变得易于理解:如果我们知道x : σ ∈ Γ(即x在某些上下文Γ中有某种类型σ),那么我们知道Γ ? x : σ(那个是,在Γ中,x具有类型σ。所以真的,这并没有告诉你任何超级有趣的东西;它只是告诉你如何使用你的上下文。

其他规则也很简单。例如,取[App]。此规则有两个条件:e?是从某种类型τ到某种类型τ'的函数,e?是类型τ的值。现在,您可以通过将e?应用于e?来了解将获得的类型!希望这不是一个惊喜:)。

下一个规则有一些新的语法。特别地,Γ, x : τ仅表示由Γ和判断x : τ组成的上下文。因此,如果我们知道变量x的类型为τ且表达式e的类型为τ',我们也知道采用x并返回e的函数的类型。这只是告诉我们如果我们弄清楚函数采用什么类型以及它返回什么类型该怎么做,所以它也不应该令人惊讶。

下一个只是告诉你如何处理let语句。如果你知道某个表达式e?的类型为τ,只要x的类型为σ,那么let表达式会将x本地绑定到类型σ的值e?的类型为τ。实际上,这只是告诉你let语句本质上允许你用一个新的绑定扩展上下文 - 这正是let的作用!

[Inst]规则处理子类型。它表示如果您的类型为σ'且它是σ的子类型(?表示部分排序关系),则该表达式的类型也为σ

最终规则涉及泛化类型。快速搁置:自由变量是一个变量,它不是由某个表达式中的let语句或lambda引入的;这个表达式现在依赖于来自其上下文的自由变量的值。规则是说如果在你的上下文中有任何变量α不是"free",那么可以肯定地说任何表达式类型你知道e : σ将具有任何α值的类型。

如何使用规则

那么,既然您已了解符号,那么您对这些规则有何看法?好吧,您可以使用这些规则来确定各种值的类型。要执行此操作,请查看您的表达式(例如f x y)并查找与您的语句匹配的结论(底部)的规则。让我们称之为你想要找到你的"目标"。在这种情况下,您将查看以e? e?结尾的规则。当你发现这一点时,你现在必须找到规则,证明这条规则的所有内容。这些东西通常对应于子表达式的类型,因此您实际上是对表达式的某些部分进行递归。您只需执行此操作,直到完成校对树,这样可以证明表达式的类型。

因此,所有这些规则都是精确地指定 - 并且在通常的数学上迂腐的细节:P-如何找出表达式的类型。

现在,如果您曾经使用Prolog,这应该听起来很熟悉 - 您实际上是像人类Prolog解释器一样计算证明树。 Prolog被称为"逻辑编程"是有原因的!这也很重要,因为我介绍H-M推理算法的第一种方法是在Prolog中实现它。这实际上非常简单,并且清楚地表明了这一点。你当然应该尝试一下。

注意:我可能在这个解释中犯了一些错误,并且如果有人指出它们会很喜欢它。我实际上会在几周内在课堂上报道,所以我会更自信:P。

好。


if somebody could at least tell me where to start looking to comprehend what this sea of symbols means

参见"编程语言的实用基础",第2章和第3章,通过判断和推导来讨论逻辑风格。整本书现已在亚马逊上市。

第2章

归纳定义

归纳定义是编程语言研究中不可或缺的工具。在本章中,我们将开发归纳定义的基本框架,并举例说明它们的使用。归纳定义包括一组用于推导各种形式的判断或断言的规则。判断是关于指定排序的一个或多个句法对象的陈述。规则规定了判决有效性的必要和充分条件,从而充分确定其含义。

2.1判决

我们从判断的概念开始,或者关于句法对象的断言。我们将利用多种形式的判断,包括以下例子:

  • n nat - n是一个自然数
  • n = n1 + n2 -n是n1和n2之和
  • τtype - τ是一种类型
  • e:τ - 表达式e具有类型τ
  • e? v - 表达式e具有值v

判断表明一个或多个句法对象具有彼此某种关系的属性或立场。财产或关系本身被称为判断形式,并且一个或多个对象在该关系中具有该属性或立场的判断被认为是该判断形式的实例。判断形式也称为谓词,构成实例的对象是其主语。我们为判断断言J持有a而写了一个J.当强调判断的主题并不重要时,(文字在这里切断)


符号来自自然演绎。

?符号被称为旋转栅门。

这6条规则非常简单。

Var规则是相当简单的规则 - 它表示如果类型环境中已存在标识符的类型,那么要推断类型,您只需从环境中获取它。

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

AbsLet是推断lambda抽象和let-in类型的规则。

Inst规则表示您可以用较不普遍的类型替换类型。


我如何理解Hindley-Milner规则?

Hindley-Milner是一组以后续演算(非自然演绎)形式出现的规则,它表明我们可以在没有明确类型声明的情况下从程序的构造中推导出(最常规)类型的程序。

符号和符号

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

  • ??是一个标识符(非正式地,变量名称)。
  • :means是一种(非正式地,实例或"is-a")。
  • ?? (sigma)是一个变量或函数的表达式。
  • 因此??:??读为"?? is-a ??"
  • ∈表示"是一个元素"
  • ?? (Gamma)是一个环境。
  • ? (断言符号)表示断言(或证明,但在上下文中"断言"读取更好。)
  • ?? ? ?? :??因此读作"??断言??,是-a ??"
  • ??是一个类型??的实际实例(元素)。
  • ?? (tau)是一种类型:基本的,可变的(??),功能??→??',或产品??×??' (这里不使用产品)
  • ?? ??→"是一种功能型?和?? ??可能是不同的类型。
  • ????。??意思是? (lambda)是一个匿名函数,它接受一个参数,??,并返回一个表达式,??。

  • 让?? = ???在???在表达中表示,???,替换???哪里?出现。

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

  • ??是一个类型变量。
  • ? ?? ??是一种类型,? (对所有)参数变量,??,返回?表达
  • ? free(??)表示不是自由类型变量的元素?在外部环境中定义。 (绑定变量是可替代的。)
  • 线上的一切都是前提,下面的一切都是结论(Per Martin-L?f)

    优先,例如

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

  • ?? :?? ∈??可写(??:??)∈??
  • ?? ? ?? :??可以写? ? (??:??)

  • ?? ?让?? = ???在??? :??
    是等价的
    ?? ? ((让(?? = ???)in ???):??)

  • ?? ? ????。?? :??→??'是等价的? ? ((????。??):( ??→??'))

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

    规则

    接下来是对规则的英语解释,每个解释都是松散的重述和解释。

    变量

    VAR Logic Diagram

    Given ?? is a type of ?? (sigma), an element of ?? (Gamma),
    conclude ?? asserts ?? is a ??.

    Ok.

    换句话说,在??,我们知道??是类型??因为??是类型??在?? ??

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

    功能应用

    APP Logic Diagram

    Given ?? asserts ??? is a functional type and ?? asserts ??? is a ??
    conclude ?? asserts applying function ??? to ??? is a type ??'

    Ok.

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

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

    功能抽象

    ABS Logic Diagram

    Given ?? and ?? of type ?? asserts ?? is a type, ??'
    conclude ?? asserts an anonymous function, ?? of ?? returning expression, ?? is of type ??→??'.

    Ok.

    再次,当我们看到一个需要的功能??并返回一个表达式??,我们知道它的类型?? ?? ?? ??因为?? (a ??)断言??是一个??'。

    如果我们知道??是类型??因而表达?是'??'的类型,然后是??的函数?回归表达?是?? ?? ?? ??的类型。

    让变量声明

    LET Logic Diagram

    Given ?? asserts ???, of type ??, and ?? and ??, of type ??, asserts ??? of type ??
    conclude ?? asserts let ??=??? in ??? of type ??

    Ok.

    松散地,??一定会 ???在??? (a ??)因为???是一个??,??是??断言???是一个?

    这意味着如果我们有一个表达式???这是一个 ?? (是变量或函数),有些名称,??,也是??,和表达式???类型??,那么我们可以替代???为?? ??哪里出现???。

    实例化

    INST Logic Diagram

    Given ?? asserts ?? of type ??' and ??' is a subtype of ??
    conclude ?? asserts ?? is of type ??

    Ok.

    一种表达, ??是父类型??因为表达??是子类型??'和??是''的父类型。

    如果实例的类型是另一种类型的子类型,那么它也是该超类型的实例 - 更通用的类型。

    概括

    GEN Logic Diagram

    Given ?? asserts ?? is a ?? and ?? is not an element of the free variables of ??,
    conclude ?? asserts ??, type for all argument expressions ?? returning a ?? expression

    Ok.

    所以总的来说,??键入?对于所有参数变量(??)返回??,因为我们知道??是??和??不是一个自由变量。

    这意味着我们可以将程序概括为接受所有类型的参数,这些参数尚未绑定在包含范围内(非非本地变量)。这些绑定变量是可替代的。

    把它们放在一起

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

  • 我们程序的原子元素(变量),
  • 函数返回的值(Function Application),
  • 功能构造(功能抽象),
  • 让绑定(让变量声明),
  • 父类型的实例(实例化),和
  • 所有表达式(泛化)。
  • 结论

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

    好。


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

    查看Γ作为关于表达式类型的知识,实现为一组表达式和类型,e:σ。

    旋转门?意味着从左边的知识中,我们可以推断出右边的内容。

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

    第二条规则[App]可以读取:
    如果我们从Γ可以推断出e_0具有类型τ→τ',并且我们从Γ可以推断出e_1具有类型τ,那么我们从Γ可以推断出e_0 e_1具有类型τ'。

    写Γ,e:σ而不是Γ∪{e:σ}是很常见的。

    因此可以读取第三条规则[Abs]:
    如果我们从Γ延伸到x:τ可以推导出e具有类型τ',那么我们从Γ可以推导出λx.e具有类型τ→τ'。

    第四条规则[让]留作练习。 :-)

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

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