Records为 STLC 添加记录体
Set Warnings "-notation-overridden,-parsing".
From Coq Require Import Strings.String.
From PLF Require Import Maps.
From PLF Require Import Imp.
From PLF Require Import Smallstep.
From PLF Require Import Stlc.
添加记录体
t ::= 项: | {i1=t1, ..., in=tn} 记录 | t.i 投影 | ... v ::= 值: | {i1=v1, ..., in=vn} 记录值 | ... T ::= 类型: | {i1:T1, ..., in:Tn} 记录类型 | ...归约:
ti ==> ti' | (ST_Rcd) |
{i1=v1, ..., im=vm, in=tn, ...} ==> {i1=v1, ..., im=vm, in=tn', ...} |
t1 ==> t1' | (ST_Proj1) |
t1.i ==> t1'.i |
(ST_ProjRcd) | |
{..., i=vi, ...}.i ==> vi |
Gamma ⊢ t1 : T1 ... Gamma ⊢ tn : Tn | (T_Rcd) |
Gamma ⊢ {i1=t1, ..., in=tn} : {i1:T1, ..., in:Tn} |
Gamma ⊢ t : {..., i:Ti, ...} | (T_Proj) |
Gamma ⊢ t.i : Ti |
Module FirstTry.
Definition alist (X : Type) := list (string × X).
Inductive ty : Type :=
| Base : string → ty
| Arrow : ty → ty → ty
| TRcd : (alist ty) → ty.
然而,我们在这里遇到了 Coq 的限制:该类型无法自动给出我们期望的归纳法则,
也就是说,TRcd 的情况中的归纳假设并未给出关于列表中的 ty 元素的任何信息,
因此它对我们想要做的证明毫无用处。
(* Check ty_ind.
====>
ty_ind :
forall P : ty -> Prop,
(forall i : id, P (Base i)) ->
(forall t : ty, P t -> forall t0 : ty, P t0
-> P (Arrow t t0)) ->
(forall a : alist ty, P (TRcd a)) -> (* ??? *)
forall t : ty, P t
*)
End FirstTry.
让 Coq 为我们生成更好的归纳法则是可能的,但是实现它的具体细节并不是很漂亮,
我们得到的法则也不如 Coq 为简单的 Inductive 定义生成的法则那么直观易用。
幸好,我们还能用另一种方式来形式化记录体,某种意义上说,它甚至更加简单和自然:
我们并不使用 Coq 的标准 list 类型,而是在类型的语法中直接包含它的构造子
(“nil”和“cons”)。
Inductive ty : Type :=
| Base : string → ty
| Arrow : ty → ty → ty
| RNil : ty
| RCons : string → ty → ty → ty.
与此类似,在“项”这一层上,我们有空记录体构造子 trnil,
以及将单个字段添加到字段列表之首的构造子 rcons。
Inductive tm : Type :=
| var : string → tm
| app : tm → tm → tm
| abs : string → ty → tm → tm
(* 记录体 *)
| rproj : tm → string → tm
| trnil : tm
| rcons : string → tm → tm → tm.
一些例子……
Open Scope string_scope.
Notation a := "a".
Notation f := "f".
Notation g := "g".
Notation l := "l".
Notation A := (Base "A").
Notation B := (Base "B").
Notation k := "k".
Notation i1 := "i1".
Notation i2 := "i2".
Notation a := "a".
Notation f := "f".
Notation g := "g".
Notation l := "l".
Notation A := (Base "A").
Notation B := (Base "B").
Notation k := "k".
Notation i1 := "i1".
Notation i2 := "i2".
{ i1:A }
(* Check (RCons i1 A RNil). *)
{ i1:A→B, i2:A }
(* Check (RCons i1 (Arrow A B)
(RCons i2 A RNil)). *)
该记录类型的「尾部(tail)」并不是一个真正的记录类型!
我们要重构我们的类型断言,让 weird_type 这种病态的(ill-formed)类型不能被赋项。
为此,我们定义了断言 record_ty 和 record_tm 来刻画了记录体的类型和项,
以及 well_formed_ty 用于排除劣构的类型。
首先,如果某个类型在最外层只用 RNil 和 RCons 构造,那么它是一个记录。
Inductive record_ty : ty → Prop :=
| RTnil :
record_ty RNil
| RTcons : ∀ i T1 T2,
record_ty (RCons i T1 T2).
据此,我们可以定义良构的(well-formed)类型。
Inductive well_formed_ty : ty → Prop :=
| wfBase : ∀ i,
well_formed_ty (Base i)
| wfArrow : ∀ T1 T2,
well_formed_ty T1 →
well_formed_ty T2 →
well_formed_ty (Arrow T1 T2)
| wfRNil :
well_formed_ty RNil
| wfRCons : ∀ i T1 T2,
well_formed_ty T1 →
well_formed_ty T2 →
record_ty T2 →
well_formed_ty (RCons i T1 T2).
Hint Constructors record_ty well_formed_ty.
需注意 record_ty 不是递归的:它只检查最外层的构造子。另一方面,
well_formed_ty 这一性质就其“每个记录体的尾部(即 RCons 的第二个参数)
都是记录体”的意义而言,保证了整个类型是良构的。
当然,除了类型以外,我们还要考虑劣构的项。不过类型检查无需一个额外的
well_formed_tm 定义就能排除它们,因为项的结构是经过了检查的。
我们需要的只是类似 record_ty 的定义,即如果某个项以 trnil 和 rcons
构造,那么它就是记录项。
Inductive record_tm : tm → Prop :=
| rtnil :
record_tm trnil
| rtcons : ∀ i t1 t2,
record_tm (rcons i t1 t2).
Hint Constructors record_tm.
Fixpoint subst (x:string) (s:tm) (t:tm) : tm :=
match t with
| var y ⇒ if eqb_string x y then s else t
| abs y T t1 ⇒ abs y T
(if eqb_string x y then t1 else (subst x s t1))
| app t1 t2 ⇒ app (subst x s t1) (subst x s t2)
| rproj t1 i ⇒ rproj (subst x s t1) i
| trnil ⇒ trnil
| rcons i t1 tr1 ⇒ rcons i (subst x s t1) (subst x s tr1)
end.
Notation "'[' x ':=' s ']' t" := (subst x s t) (at level 20).
Inductive value : tm → Prop :=
| v_abs : ∀ x T11 t12,
value (abs x T11 t12)
| v_rnil : value trnil
| v_rcons : ∀ i v1 vr,
value v1 →
value vr →
value (rcons i v1 vr).
Hint Constructors value.
为了定义归约,我们需要一个辅助函数来提取记录项中的某个字段:
Fixpoint tlookup (i:string) (tr:tm) : option tm :=
match tr with
| rcons i' t tr' ⇒ if eqb_string i i' then Some t else tlookup i tr'
| _ ⇒ None
end.
step 函数会在投影规则中使用该项一级的查找函数。
Reserved Notation "t1 '-->' t2" (at level 40).
Inductive step : tm → tm → Prop :=
| ST_AppAbs : ∀ x T11 t12 v2,
value v2 →
(app (abs x T11 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_Proj1 : ∀ t1 t1' i,
t1 --> t1' →
(rproj t1 i) --> (rproj t1' i)
| ST_ProjRcd : ∀ tr i vi,
value tr →
tlookup i tr = Some vi →
(rproj tr i) --> vi
| ST_Rcd_Head : ∀ i t1 t1' tr2,
t1 --> t1' →
(rcons i t1 tr2) --> (rcons i t1' tr2)
| ST_Rcd_Tail : ∀ i v1 tr2 tr2',
value v1 →
tr2 --> tr2' →
(rcons i v1 tr2) --> (rcons i v1 tr2')
where "t1 '-->' t2" := (step t1 t2).
Notation multistep := (multi step).
Notation "t1 '-->*' t2" := (multistep t1 t2) (at level 40).
Hint Constructors step.
定型
Fixpoint Tlookup (i:string) (Tr:ty) : option ty :=
match Tr with
| RCons i' T Tr' ⇒
if eqb_string i i' then Some T else Tlookup i Tr'
| _ ⇒ None
end.
Definition context := partial_map ty.
Reserved Notation "Gamma '⊢' t '∈' T" (at level 40).
Inductive has_type : context → tm → ty → Prop :=
| T_Var : ∀ Gamma x T,
Gamma x = Some T →
well_formed_ty T →
Gamma ⊢ (var x) \in T
| T_Abs : ∀ Gamma x T11 T12 t12,
well_formed_ty T11 →
(update Gamma x T11) ⊢ t12 \in T12 →
Gamma ⊢ (abs x T11 t12) \in (Arrow T11 T12)
| T_App : ∀ T1 T2 Gamma t1 t2,
Gamma ⊢ t1 \in (Arrow T1 T2) →
Gamma ⊢ t2 \in T1 →
Gamma ⊢ (app t1 t2) \in T2
(* records: *)
| T_Proj : ∀ Gamma i t Ti Tr,
Gamma ⊢ t \in Tr →
Tlookup i Tr = Some Ti →
Gamma ⊢ (rproj t i) \in Ti
| T_RNil : ∀ Gamma,
Gamma ⊢ trnil \in RNil
| T_RCons : ∀ Gamma i t T tr Tr,
Gamma ⊢ t \in T →
Gamma ⊢ tr \in Tr →
record_ty Tr →
record_tm tr →
Gamma ⊢ (rcons i t tr) \in (RCons i T Tr)
where "Gamma '⊢' t '∈' T" := (has_type Gamma t T).
Hint Constructors has_type.
示例
练习:2 星, standard (examples)
完成下面的证明。在此证明中可随意使用 Coq 的自动化特性。然而, 如果你不熟悉类型系统是如何工作的,那么应该先用基本策略来完成证明 (比如使用 apply 而非 eapply),再试着用自动化特性来简化它。 在证明之前,请先确保你理解它的意思。Lemma typing_example_2 :
empty ⊢
(app (abs a (RCons i1 (Arrow A A)
(RCons i2 (Arrow B B)
RNil))
(rproj (var a) i2))
(rcons i1 (abs a A (var a))
(rcons i2 (abs a B (var a))
trnil))) \in
(Arrow B B).
Proof.
(* 请在此处解答 *) Admitted.
Example typing_nonexample :
¬ ∃ T,
(update empty a (RCons i2 (Arrow A A)
RNil)) ⊢
(rcons i1 (abs a B (var a)) (var a)) \in
T.
Proof.
(* 请在此处解答 *) Admitted.
Example typing_nonexample_2 : ∀ y,
¬ ∃ T,
(update empty y A) ⊢
(app (abs a (RCons i1 A RNil)
(rproj (var a) i1))
(rcons i1 (var y) (rcons i2 (var y) trnil))) \in
T.
Proof.
(* 请在此处解答 *) Admitted.
Lemma wf_rcd_lookup : ∀ i T Ti,
well_formed_ty T →
Tlookup i T = Some Ti →
well_formed_ty Ti.
Proof with eauto.
intros i T.
induction T; intros; try solve_by_invert.
- (* RCons *)
inversion H. subst. unfold Tlookup in H0.
destruct (eqb_string i s)...
inversion H0. subst... Qed.
intros i T.
induction T; intros; try solve_by_invert.
- (* RCons *)
inversion H. subst. unfold Tlookup in H0.
destruct (eqb_string i s)...
inversion H0. subst... Qed.
Lemma step_preserves_record_tm : ∀ tr tr',
record_tm tr →
tr --> tr' →
record_tm tr'.
Proof.
intros tr tr' Hrt Hstp.
inversion Hrt; subst; inversion Hstp; subst; auto.
Qed.
intros tr tr' Hrt Hstp.
inversion Hrt; subst; inversion Hstp; subst; auto.
Qed.
Lemma has_type__wf : ∀ Gamma t T,
Gamma ⊢ t \in T → well_formed_ty T.
Proof with eauto.
intros Gamma t T Htyp.
induction Htyp...
- (* T_App *)
inversion IHHtyp1...
- (* T_Proj *)
eapply wf_rcd_lookup...
Qed.
intros Gamma t T Htyp.
induction Htyp...
- (* T_App *)
inversion IHHtyp1...
- (* T_Proj *)
eapply wf_rcd_lookup...
Qed.
依字段查表
- 若 i = i0,那么根据 Tlookup i (RCons i0 T Tr) = Some Ti,我们有
T = Ti。它根据 t 本身满足此定理得出。
- 另一方面,假设 i ≠ i0。那么
Tlookup i T = Tlookup i Tr
tlookup i t = tlookup i tr,
Lemma lookup_field_in_value : ∀ v T i Ti,
value v →
empty ⊢ v \in T →
Tlookup i T = Some Ti →
∃ ti, tlookup i v = Some ti ∧ empty ⊢ ti \in Ti.
Proof with eauto.
intros v T i Ti Hval Htyp Hget.
remember (@empty ty) as Gamma.
induction Htyp; subst; try solve_by_invert...
- (* T_RCons *)
simpl in Hget. simpl. destruct (eqb_string i i0).
+ (* i is first *)
simpl. inversion Hget. subst.
∃ t...
+ (* get tail *)
destruct IHHtyp2 as [vi [Hgeti Htypi]]...
inversion Hval... Qed.
intros v T i Ti Hval Htyp Hget.
remember (@empty ty) as Gamma.
induction Htyp; subst; try solve_by_invert...
- (* T_RCons *)
simpl in Hget. simpl. destruct (eqb_string i i0).
+ (* i is first *)
simpl. inversion Hget. subst.
∃ t...
+ (* get tail *)
destruct IHHtyp2 as [vi [Hgeti Htypi]]...
inversion Hval... Qed.
Theorem progress : ∀ t T,
empty ⊢ t \in T →
value t ∨ ∃ t', t --> t'.
Proof with eauto.
(* 定理:假设 empty ⊢ t : T。那么以下二者之一成立:
1. t 为值,或者
2. 对某个 t',有 t --> t'。
证明:对给定的定型导出式作归纳。 *)
intros t T Ht.
remember (@empty ty) as Gamma.
generalize dependent HeqGamma.
induction Ht; intros HeqGamma; subst.
- (* T_Var *)
(* 一给定的定型导出式的最后一条规则不能为 T_Var,因为它不应是
empty ⊢ x : T 的情况(因为上下文为空)。 *)
inversion H.
- (* T_Abs *)
(* 若最后应用的规则为 T_Abs,则有 t = abs x T11 t12,它是一个值。 *)
left...
- (* T_App *)
(* 若最后应用的规则为 T_App,则有 t = t1 t2。这点可由以下规则的形式得到:
empty ⊢ t1 : T1 → T2
empty ⊢ t2 : T1
根据归纳假设,t1 和 t2 要么是值,要么可再推进一步。 *)
right.
destruct IHHt1; subst...
+ (* t1 为值 *)
destruct IHHt2; subst...
× (* t2 为值 *)
(* 若 t1 和 t2 均为值,那么我们知道
t1 = abs x T11 t12,因为“抽象”是唯一具有箭头类型的值。
然而根据 ST_AppAbs,(abs x T11 t12) t2 --> [x:=t2]t12。 *)
inversion H; subst; try solve_by_invert.
∃ ([x:=t2]t12)...
× (* t2 推进一步 *)
(* 若 t1 为值且 t2 --> t2',那么根据 ST_App2,
t1 t2 --> t1 t2'。 *)
destruct H0 as [t2' Hstp]. ∃ (app t1 t2')...
+ (* t1 推进一步 *)
(* 最后,若 t1 --> t1',那么根据 ST_App1,t1 t2 --> t1' t2。 *)
destruct H as [t1' Hstp]. ∃ (app t1' t2)...
- (* T_Proj *)
(* 若给定导出式的最后一条规则为 T_Proj,那么
t = rproj t i 且 empty ⊢ t : (TRcd Tr)
根据归纳假设,t 要么是值,要么可以推进一步。 *)
right. destruct IHHt...
+ (* rcd 为值 *)
(* 若 t 为值,那么我们可使用引理 lookup_field_in_value
来证明对某个 ti 而言 tlookup i t = Some ti。根据
ST_ProjRcd 可得 rproj i t --> ti。 *)
destruct (lookup_field_in_value _ _ _ _ H0 Ht H)
as [ti [Hlkup _]].
∃ ti...
+ (* rcd 推进一步 *)
(* 另一方面,若 t --> t',那么根据 ST_Proj1 可得
rproj t i --> rproj t' i。 *)
destruct H0 as [t' Hstp]. ∃ (rproj t' i)...
- (* T_RNil *)
(* 若给定导出式中的最后一条规则为 T_RNil,那么 t = trnil,为值。 *)
left...
- (* T_RCons *)
(* 若最后一条规则为 T_RCons,那么 t = rcons i t tr 且
empty ⊢ t : T
empty ⊢ tr : Tr
根据归纳法则,t 和 tr 要么是值,要么可再推进一步。 *)
destruct IHHt1...
+ (* head 为值 *)
destruct IHHt2; try reflexivity.
× (* tail 为值 *)
(* 若 t 和 tr 均为值,那么 rcons i t tr 也为值。 *)
left...
× (* tail 推进一步 *)
(* 若 t 为值且 tr --> tr',那么根据 ST_Rcd_Tail,
rcons i t tr --> rcons i t tr'。 *)
right. destruct H2 as [tr' Hstp].
∃ (rcons i t tr')...
+ (* head 推进一步 *)
(* 若 t --> t',那么根据 ST_Rcd_Head,
rcons i t tr --> rcons i t' tr。 *)
right. destruct H1 as [t' Hstp].
∃ (rcons i t' tr)... Qed.
(* 定理:假设 empty ⊢ t : T。那么以下二者之一成立:
1. t 为值,或者
2. 对某个 t',有 t --> t'。
证明:对给定的定型导出式作归纳。 *)
intros t T Ht.
remember (@empty ty) as Gamma.
generalize dependent HeqGamma.
induction Ht; intros HeqGamma; subst.
- (* T_Var *)
(* 一给定的定型导出式的最后一条规则不能为 T_Var,因为它不应是
empty ⊢ x : T 的情况(因为上下文为空)。 *)
inversion H.
- (* T_Abs *)
(* 若最后应用的规则为 T_Abs,则有 t = abs x T11 t12,它是一个值。 *)
left...
- (* T_App *)
(* 若最后应用的规则为 T_App,则有 t = t1 t2。这点可由以下规则的形式得到:
empty ⊢ t1 : T1 → T2
empty ⊢ t2 : T1
根据归纳假设,t1 和 t2 要么是值,要么可再推进一步。 *)
right.
destruct IHHt1; subst...
+ (* t1 为值 *)
destruct IHHt2; subst...
× (* t2 为值 *)
(* 若 t1 和 t2 均为值,那么我们知道
t1 = abs x T11 t12,因为“抽象”是唯一具有箭头类型的值。
然而根据 ST_AppAbs,(abs x T11 t12) t2 --> [x:=t2]t12。 *)
inversion H; subst; try solve_by_invert.
∃ ([x:=t2]t12)...
× (* t2 推进一步 *)
(* 若 t1 为值且 t2 --> t2',那么根据 ST_App2,
t1 t2 --> t1 t2'。 *)
destruct H0 as [t2' Hstp]. ∃ (app t1 t2')...
+ (* t1 推进一步 *)
(* 最后,若 t1 --> t1',那么根据 ST_App1,t1 t2 --> t1' t2。 *)
destruct H as [t1' Hstp]. ∃ (app t1' t2)...
- (* T_Proj *)
(* 若给定导出式的最后一条规则为 T_Proj,那么
t = rproj t i 且 empty ⊢ t : (TRcd Tr)
根据归纳假设,t 要么是值,要么可以推进一步。 *)
right. destruct IHHt...
+ (* rcd 为值 *)
(* 若 t 为值,那么我们可使用引理 lookup_field_in_value
来证明对某个 ti 而言 tlookup i t = Some ti。根据
ST_ProjRcd 可得 rproj i t --> ti。 *)
destruct (lookup_field_in_value _ _ _ _ H0 Ht H)
as [ti [Hlkup _]].
∃ ti...
+ (* rcd 推进一步 *)
(* 另一方面,若 t --> t',那么根据 ST_Proj1 可得
rproj t i --> rproj t' i。 *)
destruct H0 as [t' Hstp]. ∃ (rproj t' i)...
- (* T_RNil *)
(* 若给定导出式中的最后一条规则为 T_RNil,那么 t = trnil,为值。 *)
left...
- (* T_RCons *)
(* 若最后一条规则为 T_RCons,那么 t = rcons i t tr 且
empty ⊢ t : T
empty ⊢ tr : Tr
根据归纳法则,t 和 tr 要么是值,要么可再推进一步。 *)
destruct IHHt1...
+ (* head 为值 *)
destruct IHHt2; try reflexivity.
× (* tail 为值 *)
(* 若 t 和 tr 均为值,那么 rcons i t tr 也为值。 *)
left...
× (* tail 推进一步 *)
(* 若 t 为值且 tr --> tr',那么根据 ST_Rcd_Tail,
rcons i t tr --> rcons i t tr'。 *)
right. destruct H2 as [tr' Hstp].
∃ (rcons i t tr')...
+ (* head 推进一步 *)
(* 若 t --> t',那么根据 ST_Rcd_Head,
rcons i t tr --> rcons i t' tr。 *)
right. destruct H1 as [t' Hstp].
∃ (rcons i t' tr)... Qed.
Inductive appears_free_in : string → tm → Prop :=
| afi_var : ∀ x,
appears_free_in x (var x)
| afi_app1 : ∀ x t1 t2,
appears_free_in x t1 → appears_free_in x (app t1 t2)
| afi_app2 : ∀ x t1 t2,
appears_free_in x t2 → appears_free_in x (app t1 t2)
| afi_abs : ∀ x y T11 t12,
y ≠ x →
appears_free_in x t12 →
appears_free_in x (abs y T11 t12)
| afi_proj : ∀ x t i,
appears_free_in x t →
appears_free_in x (rproj t i)
| afi_rhead : ∀ x i ti tr,
appears_free_in x ti →
appears_free_in x (rcons i ti tr)
| afi_rtail : ∀ x i ti tr,
appears_free_in x tr →
appears_free_in x (rcons i ti tr).
Hint Constructors appears_free_in.
Lemma context_invariance : ∀ Gamma Gamma' t S,
Gamma ⊢ t \in S →
(∀ x, appears_free_in x t → Gamma x = Gamma' x) →
Gamma' ⊢ t \in S.
Proof with eauto.
intros. generalize dependent Gamma'.
induction H;
intros Gamma' Heqv...
- (* T_Var *)
apply T_Var... rewrite <- Heqv...
- (* T_Abs *)
apply T_Abs... apply IHhas_type. intros y Hafi.
unfold update, t_update. destruct (eqb_stringP x y)...
- (* T_App *)
apply T_App with T1...
- (* T_RCons *)
apply T_RCons... Qed.
intros. generalize dependent Gamma'.
induction H;
intros Gamma' Heqv...
- (* T_Var *)
apply T_Var... rewrite <- Heqv...
- (* T_Abs *)
apply T_Abs... apply IHhas_type. intros y Hafi.
unfold update, t_update. destruct (eqb_stringP x y)...
- (* T_App *)
apply T_App with T1...
- (* T_RCons *)
apply T_RCons... Qed.
Lemma free_in_context : ∀ x t T Gamma,
appears_free_in x t →
Gamma ⊢ t \in T →
∃ T', Gamma x = Some T'.
Proof with eauto.
intros x t T Gamma Hafi Htyp.
induction Htyp; inversion Hafi; subst...
- (* T_Abs *)
destruct IHHtyp as [T' Hctx]... ∃ T'.
unfold update, t_update in Hctx.
rewrite false_eqb_string in Hctx...
Qed.
intros x t T Gamma Hafi Htyp.
induction Htyp; inversion Hafi; subst...
- (* T_Abs *)
destruct IHHtyp as [T' Hctx]... ∃ T'.
unfold update, t_update in Hctx.
rewrite false_eqb_string in Hctx...
Qed.
Lemma substitution_preserves_typing : ∀ Gamma x U v t S,
(update Gamma x U) ⊢ t \in S →
empty ⊢ v \in U →
Gamma ⊢ ([x:=v]t) \in S.
Proof with eauto.
(* 定理:若 x⊢>U;Gamma ⊢ t : S 且 empty ⊢ v : U,则
Gamma ⊢ (x:=vt) S。 *)
intros Gamma x U v t S Htypt Htypv.
generalize dependent Gamma. generalize dependent S.
(* 证明:通过对项 t 作归纳可得。大部分情况下,结论可以从归纳假设直接得到,
只有 var、abs 和 rcons 的情况需要额外证明。无法自动得到前两者,
是因为我们必须处理变量之间的相互作用。对于 rcons 的情况,
我们必须额外证明在项中作代换不改变它是否为一个记录项。 *)
induction t;
intros S Gamma Htypt; simpl; inversion Htypt; subst...
- (* var *)
simpl. rename s into y.
(* 若 t = y,我们知道
empty ⊢ v : U 且
x⊢>U; Gamma ⊢ y : S
经反演可得 update Gamma x U y = Some S。
我们需要证明 Gamma ⊢ [x:=v]y : S。
有两种情况需要考虑:x=y 或 x≠y。 *)
unfold update, t_update in H0.
destruct (eqb_stringP x y) as [Hxy|Hxy].
+ (* x=y *)
(* 若 x = y,那么我们知道 U = S,以及 [x:=v]y = v。
因此我们必须证明若 empty ⊢ v : U 则 Gamma ⊢ v : U。
我们已经证明了此定理更一般的版本,即上下文不变性! *)
subst.
inversion H0; subst. clear H0.
eapply context_invariance...
intros x Hcontra.
destruct (free_in_context _ _ S empty Hcontra)
as [T' HT']...
inversion HT'.
+ (* x<>y *)
(* 若 x ≠ y,则 Gamma y = Some S 且代换没有效果。我们可以根据
T_Var 证明 Gamma ⊢ y : S。 *)
apply T_Var...
- (* abs *)
rename s into y. rename t into T11.
(* 若 t = abs y T11 t0,那么我们知道
x⊢>U; Gamma ⊢ abs y T11 t0 : T11→T12
x⊢>U; y⊢>T11; Gamma ⊢ t0 : T12
empty ⊢ v : U
根据归纳假设,我们知道对于所有的 S Gamma,
x⊢>U; Gamma ⊢ t0 : S → Gamma ⊢ [x:=v]t0 S.
我们可以计算出
[x:=v]t = abs y T11 (if eqb_string x y then t0 else [x:=v]t0) ,
此时我们必须证明 Gamma ⊢ [x:=v]t : T11→T12。我们知道可以用
T_Abs 来证明它,因此接下来需要证明的就是:
y⊢>T11; Gamma ⊢ if eqb_string x y then t0 else [x:=v]t0 : T12
我们考虑两种情况:x = y 和 x ≠ y。 *)
apply T_Abs...
destruct (eqb_stringP x y) as [Hxy|Hxy].
+ (* x=y *)
(* 若 x = y,则代换没有作用。上下文不变性保证了
y:U,y:T11 和 Gamma,y:T11 等价。在前者的上下文中有 t0 : T12 ,
那么在后者中也是这样。 *)
eapply context_invariance...
subst.
intros x Hafi. unfold update, t_update.
destruct (eqb_string y x)...
+ (* x<>y *)
(* 若 x ≠ y,那么归纳假设和上下文不变性能让我们证明
x⊢>U; y⊢>T11; Gamma ⊢ t0 : T12 =>
y⊢>T11; x⊢>U; Gamma ⊢ t0 : T12 =>
y⊢>T11; Gamma ⊢ [x:=v]t0 : T12 *)
apply IHt. eapply context_invariance...
intros z Hafi. unfold update, t_update.
destruct (eqb_stringP y z)...
subst. rewrite false_eqb_string...
- (* rcons *)
apply T_RCons... inversion H7; subst; simpl...
Qed.
Theorem preservation : ∀ t t' T,
empty ⊢ t \in T →
t --> t' →
empty ⊢ t' \in T.
Proof with eauto.
intros t t' T HT.
(* 定理:若 empty ⊢ t : T 且 t --> t',则
empty ⊢ t' : T. *)
remember (@empty ty) as Gamma. generalize dependent HeqGamma.
generalize dependent t'.
(* 证明:对给定的定型导出式来归纳。很多情况是矛盾的(T_Var、T_Abs)
或可直接从归纳假设得出(T_RCons),我们只需证明那些“有趣”的情况。 *)
induction HT;
intros t' HeqGamma HE; subst; inversion HE; subst...
- (* T_App *)
(* 若使用的最后一条规则为 T_App,那么 t = t1 t2。
证明 t --> t' 可能用到的三条规则是 ST_App1、ST_App2 和 ST_AppAbs。
在前两种情况下,结果可直接从归纳假设得出。 *)
inversion HE; subst...
+ (* ST_AppAbs *)
(* 对于第三种情况,假设
t1 = abs x T11 t12
且
t2 = v2。我们必须证明 empty ⊢ [x:=v2]t12 : T2。
根据假设我们知道
empty ⊢ abs x T11 t12 : T1→T2
经反演可得
x:T1 ⊢ t12 : T2
我们已经证明了 substitution_preserves_typing,又根据假设,有
empty ⊢ v2 : T1
故此分支证明完毕。 *)
apply substitution_preserves_typing with T1...
inversion HT1...
- (* T_Proj *)
(* 若 T_Proj,则 t = rproj t1 i。
证明 t --> t' 需要两条规则:T_Proj1 和 T_ProjRcd。
t' 的定型可从前面情况中的归纳假设得出,因此我们只需考虑
T_ProjRcd 即可。
这里我们知道 t 为记录体值。由于使用了规则 T_Proj,
我们知道对于某些 i 和 Tr 有 empty ⊢ t \in Tr 且
Tlookup i Tr = Some Ti。接着可以应用引理 lookup_field_in_value
来查找该投影步骤所得的记录元素。 *)
destruct (lookup_field_in_value _ _ _ _ H2 HT H)
as [vi [Hget Htyp]].
rewrite H4 in Hget. inversion Hget. subst...
- (* T_RCons *)
(* 若最后一条规则为 T_RCons,那么对于某些 i、t 和满足
record_tm tr 的 tr,有 t = rcons i t tr。若该步骤为
ST_Rcd_Head,则结果可由归纳假设直接得出。若该步骤为
ST_Rcd_Tail,那么对于某些 tr2' 有 tr --> tr2',
我们还需要使用引理 step_preserves_record_tm 来证明 record_tm tr2'。 *)
apply T_RCons... eapply step_preserves_record_tm...
Qed.
☐