这个语法虽然看起来复杂,但实际上相当简单。基本思想来自形式逻辑:整个表达式是一个暗示,上半部分是假设,下半部分是结果。也就是说,如果你知道上面的表达式是真的,你就可以得出下面的表达式也是真的。
符号
另一件要记住的事情是,有些字母有传统意义;特别是,Γ表示您所处的“上下文”,即您所看到的其他事物的类型。比如Γ⊢……意思是“表达……当您知道Γ中每个表达式的类型时。
⊢符号本质上意味着你可以证明一些东西。所以Γ⊢…这句话的意思是“我可以证明……”在Γ上下文中。这些陈述也被称为类型判断。
另一件需要记住的事情是:在数学中,就像ML和Scala一样,x: σ意味着x具有类型σ。你可以像Haskell的x:: σ一样读取它。
每条规则的含义
因此,知道了这一点,第一个表达式就容易理解了:如果我们知道x: σ∈Γ(即,x在某些上下文中Γ有某种类型σ),那么我们就知道Γ⊢x: σ(即,在Γ中,x有类型σ)。实际上,这并没有告诉你什么特别有趣的事情;它只是告诉你如何使用上下文。
其他规则也很简单。以[App]为例。这个规则有两个条件:e₀是一个从某种类型τ到某种类型τ'的函数,e₁是一个类型τ的值。现在,通过应用e₀to e₁,您就知道您将得到什么类型的₁了!希望这不是一个惊喜:)。
下一个规则有一些新的语法。特别是Γ, x: τ仅仅意味着由Γ和判断x: τ组成的上下文。所以,如果我们知道变量x的类型是τ,表达式e的类型是τ',我们也知道接受x并返回e的函数的类型。这只是告诉我们,如果我们已经弄清楚一个函数接受什么类型以及它返回什么类型,应该怎么做,所以这也不应该令人惊讶。
下一个告诉你如何处理let语句。如果您知道某个表达式e₁的类型是τ,只要x的类型是σ,那么let表达式将x局部绑定到σ类型值将使e₁的类型是τ。实际上,这只是告诉您,let语句本质上允许您使用新绑定扩展上下文—这正是let所做的!
[Inst]规则处理子类型。它说,如果你有一个类型为σ'的值,并且它是σ的子类型(⊑表示偏序关系),那么这个表达式也是类型σ的。
最后一条规则处理泛化类型。顺便说一句:自由变量是指表达式中没有由let语句或lambda引入的变量;这个表达式现在依赖于上下文中自由变量的值。这个规则说的是,如果在你的上下文中有某个变量α不是“自由的”,那么可以肯定地说,任何表达式,其类型你知道e: σ,对于α的任何值都有该类型。
如何使用规则
So, now that you understand the symbols, what do you do with these rules? Well, you can use these rules to figure out the type of various values. To do this, look at your expression (say f x y) and find a rule that has a conclusion (the bottom part) that matches your statement. Let's call the thing you're trying to find your "goal". In this case, you would look at the rule that ends in e₀ e₁. When you've found this, you now have to find rules proving everything above the line of this rule. These things generally correspond to the types of sub-expressions, so you're essentially recursing on parts of the expression. You just do this until you finish your proof tree, which gives you a proof of the type of your expression.
因此,所有这些规则所做的就是精确地指定——并且以通常的数学迂腐的细节——如何计算表达式的类型。
现在,如果您曾经使用过Prolog,那么这听起来应该很熟悉——您实际上是像人类Prolog解释器一样计算证明树。Prolog被称为“逻辑编程”是有原因的!这一点也很重要,因为我接触H-M推理算法的第一种方式是在Prolog中实现它。这实际上非常简单,并且清楚地说明了发生了什么。你当然应该试试。
注意:我可能在这个解释中犯了一些错误,如果有人能指出来,我会很高兴的。我会在几周后的课上讲到这个,到时我会更有信心,P。