Stlc简单类型 Lambda-演算
Set Warnings "-notation-overridden,-parsing".
From Coq Require Import Strings.String.
From PLF Require Import Maps.
From PLF Require Import Smallstep.
简介
- 变量
- 函数抽象
- 应用
t ::= x variable
| \x:T.t abstraction
| t t application
| tru constant true
| fls constant false
| test t then t else t conditional
- \x:Bool. x
- (\x:Bool. x) tru
- \x:Bool. test x then fls else tru
- \x:Bool. tru
- \x:Bool. \y:Bool. x
- (\x:Bool. \y:Bool. x) fls tru
- \f:Bool→Bool. f (f tru)
- (\f:Bool→Bool. f (f tru)) (\x:Bool. fls)
T ::= Bool
| T → T
- \x:Bool. fls 有类型 Bool→Bool
- \x:Bool. x 有类型 Bool→Bool
- (\x:Bool. x) tru 有类型 Bool
- \x:Bool. \y:Bool. x 有类型 Bool→Bool→Bool
(即 Bool → (Bool→Bool))
- (\x:Bool. \y:Bool. x) fls 有类型 Bool→Bool
- (\x:Bool. \y:Bool. x) fls tru 有类型 Bool
Inductive tm : Type :=
| var : string → tm
| app : tm → tm → tm
| abs : string → ty → tm → tm
| tru : tm
| fls : tm
| test : tm → tm → tm → tm.
请注意一个形如 \x:T.t 的抽象(形式化地讲是 abs x T t)包含其参数
T 的类型注释,相反在 Coq(以及其他函数式语言,比如 ML,Haskell等)中,
会使用类型推导来填补这些类型注释。我们在此不考虑类型推导。
一些例子……
Open Scope string_scope.
Definition x := "x".
Definition y := "y".
Definition z := "z".
Hint Unfold x.
Hint Unfold y.
Hint Unfold z.
idB = \x:Bool. x
idBB = \x:Bool→Bool. x
idBBBB = \x:(Bool→Bool) → (Bool→Bool). x
k = \x:Bool. \y:Bool. x
notB = \x:Bool. test x then fls else tru
(我们使用 Notation 而非 Definition 使 atuo 更有效。)
操作语义
值
- 我们可以说仅当 t 是值时 \x:T. t 是值——也即,仅当函数体已经被归约
(在不知道被应用的参数是什么的情况下尽可能地归约)。
- 或者,我们可以说不论 t 是不是值,\x:T. t 都是一个值——换句话说, 归约止于抽象。
Compute (fun x:bool ⇒ 3 + 4)
fun x:bool ⇒ 7
Inductive value : tm → Prop :=
| v_abs : ∀ x T t,
value (abs x T t)
| v_tru :
value tru
| v_fls :
value fls.
Hint Constructors value.
最后,我们必须考虑什么构成了一个'完整(complete)'的程序。
直观地讲,一个“完整的程序”不能包含未定义的变量。我们很快会看到如何定义 STLC
项中的'自由(free)'变量。一个完整的程序是'闭合的(closed)'——也就是说,
它不含有自由变量。
(相反,含有自由变量的项一般被叫做'开放项(open term)'。)
由于我们决定不对抽象内的表达式进行归约,因此也不必担心变量是否是值这个问题。
因为我们总是“从外向内”地归约程序,这意味着 step 关系仅会处理闭合项。
替换
(\x:Bool. test x then tru else x) fls
test fls then tru else fls
- [x:=tru] (test x then x else fls)
产生 test tru then tru else fls
- [x:=tru] x 产生 tru
- [x:=tru] (test x then x else y) 产生 test tru then tru else y
- [x:=tru] y 产生 y
- [x:=tru] fls yields fls (vacuous substitution)
- [x:=tru] (\y:Bool. test y then x else fls)
产生 \y:Bool. test y then tru else fls
- [x:=tru] (\y:Bool. x) 产生 \y:Bool. tru
- [x:=tru] (\y:Bool. y) 产生 \y:Bool. y
- [x:=tru] (\x:Bool. x) 产生 \x:Bool. x
[x:=s]x = s
[x:=s]y = y if x ≠ y
[x:=s](\x:T11. t12) = \x:T11. t12
[x:=s](\y:T11. t12) = \y:T11. [x:=s]t12 if x ≠ y
[x:=s](t1 t2) = ([x:=s]t1) ([x:=s]t2)
[x:=s]tru = tru
[x:=s]fls = fls
[x:=s](test t1 then t2 else t3) =
test [x:=s]t1 then [x:=s]t2 else [x:=s]t3
Reserved Notation "'[' x ':=' s ']' t" (at level 20).
Fixpoint subst (x : string) (s : tm) (t : tm) : tm :=
match t with
| var x' ⇒
if eqb_string x x' then s else t
| abs x' T t1 ⇒
abs x' T (if eqb_string x x' then t1 else ([x:=s] t1))
| app t1 t2 ⇒
app ([x:=s] t1) ([x:=s] t2)
| tru ⇒
tru
| fls ⇒
fls
| test t1 t2 t3 ⇒
test ([x:=s] t1) ([x:=s] t2) ([x:=s] t3)
end
where "'[' x ':=' s ']' t" := (subst x s t).
'技术注解':如果我们考虑用于替换掉某个变量的项 s 其本身也含有自由变量,
那么定义替换将会变得困难一点。由于我们仅对定义在'闭合'项(也即像 \x:Bool. x
这种绑定了内部全部变量的项)上的 step 关系有兴趣,我们可以规避这个额外的复杂性,
但是当形式化构造更丰富的语言时,我们必须考虑这一点。
比如说,使用上面的替换定义将 t = \r:Bool. z(其中 r 为绑定变量)中的
z 替换为'开放'项 s = \x:Bool. r(其中 r 是引用了某个全局资源的自由变量),
我们会得到 \r:Bool. \x:Bool. r。这时,s 中的自由引用 r 被 t 所引入
的绑定所“捕获”。
为什么这是件坏事呢?因为它违反了“绑定变量的名字不应当改变语义”这个原则。打个比方,
如果把 t 的绑定变量重命名,比如说,t' = \w:Bool. z,那么 [z:=s]t'
会产生 \w:Bool. \x:Bool. r,其行为和 [z:=s]t = \r:Bool. \x:Bool. r
并不相同。这意味着,重命名绑定变量改变了 t 在替换时的行为。
对于这个问题,更详细的讨论可参考 [Aydemir 2008]。
练习:3 星, standard (substi_correct)
上面我们使用了 Coq 的 Fixpoint 功能将替换定义为一个'函数'。 假设,现在我们想要将替换定义为一个归纳的'关系' substi。作为开始,我们给出了 Inductive 定义的头部和其中一个构造子;你的任务是完成剩下的构造子,并证明 你的定义同替换函数的定义相一致。Inductive substi (s : tm) (x : string) : tm → tm → Prop :=
| s_var1 :
substi s x (var x) s
(* 请在此处解答 *)
.
Hint Constructors substi.
Theorem substi_correct : ∀ s x t t',
[x:=s]t = t' ↔ substi s x t t'.
Proof.
(* 请在此处解答 *) Admitted.
☐
归约
(\x:T.t12) v2 --> [x:=v2]t12
value v2 | (ST_AppAbs) |
(\x:T.t12) v2 --> [x:=v2]t12 |
t1 --> t1' | (ST_App1) |
t1 t2 --> t1' t2 |
value v1 | |
t2 --> t2' | (ST_App2) |
v1 t2 --> v1 t2' |
(ST_TestTru) | |
(test tru then t1 else t2) --> t1 |
(ST_TestFls) | |
(test fls then t1 else t2) --> t2 |
t1 --> t1' | (ST_Test) |
(test t1 then t2 else t3) --> (test t1' then t2 else t3) |
Reserved Notation "t1 '-->' t2" (at level 40).
Inductive step : tm → tm → Prop :=
| ST_AppAbs : ∀ x T t12 v2,
value v2 →
(app (abs x T t12) v2) --> [x:=v2]t12
| ST_App1 : ∀ t1 t1' t2,
t1 --> t1' →
app t1 t2 --> app t1' t2
| ST_App2 : ∀ v1 t2 t2',
value v1 →
t2 --> t2' →
app v1 t2 --> app v1 t2'
| ST_TestTru : ∀ t1 t2,
(test tru t1 t2) --> t1
| ST_TestFls : ∀ t1 t2,
(test fls t1 t2) --> t2
| ST_Test : ∀ t1 t1' t2 t3,
t1 --> t1' →
(test t1 t2 t3) --> (test t1' t2 t3)
where "t1 '-->' t2" := (step t1 t2).
Hint Constructors step.
Notation multistep := (multi step).
Notation "t1 '-->*' t2" := (multistep t1 t2) (at level 40).
Lemma step_example1 :
(app idBB idB) -->* idB.
Proof.
eapply multi_step.
apply ST_AppAbs.
apply v_abs.
simpl.
apply multi_refl. Qed.
例子:
(\x:Bool→Bool. x) ((\x:Bool→Bool. x) (\x:Bool. x))
-->* \x:Bool. x
即
(idBB (idBB idB)) -->* idB.
(\x:Bool→Bool. x) ((\x:Bool→Bool. x) (\x:Bool. x))
-->* \x:Bool. x
(idBB (idBB idB)) -->* idB.
Lemma step_example2 :
(app idBB (app idBB idB)) -->* idB.
Proof.
eapply multi_step.
apply ST_App2. auto.
apply ST_AppAbs. auto.
eapply multi_step.
apply ST_AppAbs. simpl. auto.
simpl. apply multi_refl. Qed.
例子:
(\x:Bool→Bool. x)
(\x:Bool. test x then fls else tru)
tru
-->* fls
即
(idBB notB) tru -->* fls.
(\x:Bool→Bool. x)
(\x:Bool. test x then fls else tru)
tru
-->* fls
(idBB notB) tru -->* fls.
Lemma step_example3 :
app (app idBB notB) tru -->* fls.
Proof.
eapply multi_step.
apply ST_App1. apply ST_AppAbs. auto. simpl.
eapply multi_step.
apply ST_AppAbs. auto. simpl.
eapply multi_step.
apply ST_TestTru. apply multi_refl. Qed.
例子:
(\x:Bool → Bool. x)
((\x:Bool. test x then fls else tru) tru)
-->* fls
即
idBB (notB tru) -->* fls.
(请注意,虽然这个项并不会通过类型检查,我们还是可以看看它是如何归约的。)
(\x:Bool → Bool. x)
((\x:Bool. test x then fls else tru) tru)
-->* fls
idBB (notB tru) -->* fls.
Lemma step_example4 :
app idBB (app notB tru) -->* fls.
Proof.
eapply multi_step.
apply ST_App2. auto.
apply ST_AppAbs. auto. simpl.
eapply multi_step.
apply ST_App2. auto.
apply ST_TestTru.
eapply multi_step.
apply ST_AppAbs. auto. simpl.
apply multi_refl. Qed.
我们可以使用 Smallstep 一章中定义的 normalize 策略来简化这些证明。
Lemma step_example1' :
app idBB idB -->* idB.
Proof. normalize. Qed.
Lemma step_example2' :
app idBB (app idBB idB) -->* idB.
Proof. normalize. Qed.
Lemma step_example3' :
app (app idBB notB) tru -->* fls.
Proof. normalize. Qed.
Lemma step_example4' :
app idBB (app notB tru) -->* fls.
Proof. normalize. Qed.
Lemma step_example5 :
app (app idBBBB idBB) idB
-->* idB.
Proof.
(* 请在此处解答 *) Admitted.
Lemma step_example5_with_normalize :
app (app idBBBB idBB) idB
-->* idB.
Proof.
(* 请在此处解答 *) Admitted.
☐
上下文
类型关系
Gamma x = T | (T_Var) |
Gamma ⊢ x ∈ T |
(x ⊢> T11 ; Gamma) ⊢ t12 ∈ T12 | (T_Abs) |
Gamma ⊢ \x:T11.t12 ∈ T11->T12 |
Gamma ⊢ t1 ∈ T11->T12 | |
Gamma ⊢ t2 ∈ T11 | (T_App) |
Gamma ⊢ t1 t2 ∈ T12 |
(T_Tru) | |
Gamma ⊢ tru ∈ Bool |
(T_Fls) | |
Gamma ⊢ fls ∈ Bool |
Gamma ⊢ t1 ∈ Bool Gamma ⊢ t2 ∈ T Gamma ⊢ t3 ∈ T | (T_Test) |
Gamma ⊢ test t1 then t2 else t3 ∈ T |
Reserved Notation "Gamma '⊢' t '∈' T" (at level 40).
Inductive has_type : context → tm → ty → Prop :=
| T_Var : ∀ Gamma x T,
Gamma x = Some T →
Gamma ⊢ var x \in T
| T_Abs : ∀ Gamma x T11 T12 t12,
(x ⊢> T11 ; Gamma) ⊢ t12 \in T12 →
Gamma ⊢ abs x T11 t12 \in Arrow T11 T12
| T_App : ∀ T11 T12 Gamma t1 t2,
Gamma ⊢ t1 \in Arrow T11 T12 →
Gamma ⊢ t2 \in T11 →
Gamma ⊢ app t1 t2 \in T12
| T_Tru : ∀ Gamma,
Gamma ⊢ tru \in Bool
| T_Fls : ∀ Gamma,
Gamma ⊢ fls \in Bool
| T_Test : ∀ t1 t2 t3 T Gamma,
Gamma ⊢ t1 \in Bool →
Gamma ⊢ t2 \in T →
Gamma ⊢ t3 \in T →
Gamma ⊢ test t1 t2 t3 \in T
where "Gamma '⊢' t '∈' T" := (has_type Gamma t T).
Hint Constructors has_type.
Example typing_example_1 :
empty ⊢ abs x Bool (var x) \in Arrow Bool Bool.
Proof.
apply T_Abs. apply T_Var. reflexivity. Qed.
请注意,由于我们在提示数据库中添加了 has_type 构造子,因此 auto
将可以直接解决这个证明。
更多例子:
empty ⊢ \x:A. \y:A→A. y (y x)
\in A → (A→A) → A.
empty ⊢ \x:A. \y:A→A. y (y x)
\in A → (A→A) → A.
Example typing_example_2_full :
empty ⊢
(abs x Bool
(abs y (Arrow Bool Bool)
(app (var y) (app (var y) (var x))))) \in
(Arrow Bool (Arrow (Arrow Bool Bool) Bool)).
Proof.
(* 请在此处解答 *) Admitted.
☐
练习:2 星, standard (typing_example_3)
请形式化地证明以下类型导出式成立:empty ⊢ \x:Bool→B. \y:Bool→Bool. \z:Bool.
y (x z)
\in T.
Example typing_example_3 :
∃ T,
empty ⊢
(abs x (Arrow Bool Bool)
(abs y (Arrow Bool Bool)
(abs z Bool
(app (var y) (app (var x) (var z)))))) \in
T.
Proof with auto.
(* 请在此处解答 *) Admitted.
☐
¬∃ T,
empty ⊢ \x:Bool. \y:Bool, x y \in T.
Example typing_nonexample_1 :
¬ ∃ T,
empty ⊢
(abs x Bool
(abs y Bool
(app (var x) (var y)))) \in
T.
Proof.
intros Hc. destruct Hc as [T Hc].
(* The clear tactic is useful here for tidying away bits of
the context that we're not going to need again. *)
inversion Hc; subst; clear Hc.
inversion H4; subst; clear H4.
inversion H5; subst; clear H5 H4.
inversion H2; subst; clear H2.
discriminate H1.
Qed.