免责声明
根据您的要求,这是非常非正式的。
语法
在依赖类型的语言中,我们在类型级别和值级别都有一个绑定器:
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
对照参数f
receive 的类型进行检查,而不是对其进行推断和比较是否相等(这同样效率较低)。但是,如果f
是lambda,则确实需要显式类型注释(语法中省略注释,并且在任何地方都可以添加Ann Term Term
或添加λ' (σ : Term) (v : Var)
到构造函数中)。
另外,看看“ 更简单,更简单”!博客文章。