免责声明
根据您的要求,这是非常非正式的。
语法
在依赖类型的语言中,我们在类型级别和值级别都有一个绑定器:
Term = * | (∀ (Var : Term). Term) | (Term Term) | (λ Var. Term) | Var
类型良好的术语是带有附加类型的术语,我们将编写t ∈ σ或
σ
t
表示术语t具有类型σ。
打字规则
为了简单起见,我们需要在λ v. t ∈ ∀ (v : σ). τ这两个λ和∀绑定相同的变量(v在这种情况下)。
规则:
t ∈ σ is well-formed if σ ∈ * and t is in normal form (0)
*            ∈ *                                                 (1)
∀ (v : σ). τ ∈ *             -: σ ∈ *, τ ∈ *                     (2)
λ v. t       ∈ ∀ (v : σ). τ  -: t ∈ τ                            (3)
f x          ∈ SUBS(τ, v, x) -: f ∈ ∀ (v : σ). τ, x ∈ σ          (4)
v            ∈ σ             -: v was introduced by ∀ (v : σ). τ (5)
因此,*是“所有类型的类型”(1),∀从类型(2)形成类型,lambda抽象具有pi类型(3),如果v由引入∀ (v : σ). τ,则v具有类型σ(5)。
“正常形式”表示我们使用归约规则执行尽可能多的归约:
“减”法则
(λ v. b ∈ ∀ (v : σ). τ) (t ∈ σ) ~> SUBS(b, v, t) ∈ SUBS(τ, v, t)
    where `SUBS` replaces all occurrences of `v`
    by `t` in `τ` and `b`, avoiding name capture.
或在二维语法中
σ
t
表示t ∈ σ:
(∀ (v : σ). τ) σ    SUBS(τ, v, t)
                 ~>
(λ  v     . b) t    SUBS(b, v, t)
只有当术语与关联的forall量词中的变量具有相同类型时,才可以对该术语应用lambda抽象。然后,我们以与之前纯lambda演算相同的方式减少了lambda抽象和forall量词。减去值级别部分后,我们得到(4)键入规则。
一个例子
这是函数应用程序运算符:
∀ (A : *) (B : A -> *) (f : ∀ (y : A). B y) (x : A). B x
λ  A       B            f                    x     . f x
(如果未提及,我们缩写∀ (x : σ). τ为)σ -> ττx
f返回B y任何提供y的type A。我们应用f到x,这是正确的类型A,并替代y了x中∀后.,从而f x ∈ SUBS(B y, y, x)〜> f x ∈ B x。
现在让我们缩写函数应用运算符as app并将其应用于自身:
∀ (A : *) (B : A -> *). ?
λ  A       B          . app ? ? (app A B)
我放置?我们需要提供的术语。首先,我们明确引入并实例化A和B:
∀ (f : ∀ (y : A). B y) (x : A). B x
app A B
现在我们需要统一我们拥有的
∀ (f : ∀ (y : A). B y) (x : A). B x
这与
(∀ (y : A). B y) -> ∀ (x : A). B x
什么app ? ?接收
∀ (x : A'). B' x
这导致
A' ~ ∀ (y : A). B y
B' ~ λ _. ∀ (x : A). B x -- B' ignores its argument
(另请参见什么是谓语?)
我们的表达(经过重命名)变为
∀ (A : *) (B : A -> *). ?
λ  A       B          . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)
因为对于任何A,B和f(其中f ∈ ∀ (y : A). B y)
∀ (y : A). B y
app A B f
我们可以实例化A并B获取(对于任何f具有适当类型的对象)
∀ (y : ∀ (x : A). B x). ∀ (x : A). B x
app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) f
并且类型签名等效于(∀ (x : A). B x) -> ∀ (x : A). B x。
整个表达式是
∀ (A : *) (B : A -> *). (∀ (x : A). B x) -> ∀ (x : A). B x
λ  A       B          . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)
即
∀ (A : *) (B : A -> *) (f : ∀ (x : A). B x) (x : A). B x
λ  A       B            f                    x     .
    app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B) f x
毕竟,在价值水平上的减少会产生相同的app回报。
因此,虽然只需要在纯演算几步就获得app来自app app中,类型化的设置(特别为依赖类型),我们还需要关心的统一,事情变得更加复杂,甚至一些inconsitent方便(* ∈ *)。
类型检查
- 如果t是,*则按t ∈ *(1)
- 如果t是∀ (x : σ) τ,σ ∈? *,τ ∈? *(见有关注意事项∈?如下图),然后t ∈ *按(2)
- 如果t是f x,f ∈ ∀ (v : σ) τ对于σ和τ,x ∈? σ则t ∈ SUBS(τ, v, x)乘以(4)
- 如果t是一个变量v,v被引入通过∀ (v : σ). τ然后t ∈ σ通过(5)
这些都是推理规则,但是我们对lambda不能做相同的事情(对于依赖类型,类型推理是不确定的)。因此,对于lambda,我们检查(t ∈? σ)而不是推断:
- 如果t是λ v. b并对照∀ (v : σ) τ,b ∈? τ则t ∈ ∀ (v : σ) τ
- 如果t还有其他问题并进行检查,σ则推断出t使用上述函数的类型,并检查是否为σ
类型的相等性检查要求它们采用正常形式,因此要确定是否t具有类型,σ我们首先要检查σ具有type *。如果是这样,则σ可以归一化(模吉拉德悖论),并且可以归一化(因此σ由(0)形成良好的格式)。SUBS还规范化表达式以保留(0)。
这称为双向类型检查。有了它,我们不需要为每个lambda注释一个类型:如果已知f x类型f为,则x对照参数freceive 的类型进行检查,而不是对其进行推断和比较是否相等(这同样效率较低)。但是,如果f是lambda,则确实需要显式类型注释(语法中省略注释,并且在任何地方都可以添加Ann Term Term或添加λ' (σ : Term) (v : Var)到构造函数中)。
另外,看看“ 更简单,更简单”!博客文章。