SubSubtyping
A Motivating Example
Person = {name:String, age:Nat} Student = {name:String, age:Nat, gpa:Nat}
(\r:Person. (r.age)+1) {name="Pat",age=21,gpa=1}This is a shame.
Idea: Introduce subtyping, formalizing the observation that "some types are better than others."
- S is a subtype of T, written S <: T, if a value of type S can safely be used in any context where a value of type T is expected.
Subtyping and Object-Oriented Languages
- Invoking a method m of an object o on some arguments a1..an consists of projecting out the m field of o and applying it to a1..an.
- An object belonging to a subclass must provide all the
methods and fields of one belonging to a superclass, plus
possibly some more.
- Thus a subclass object can be used anywhere a superclass
object is expected.
- Very handy for organizing large libraries
Of course, real OO languages have lots of other features...
- mutable fields
- "private" and other visibility modifiers
- method inheritance
- static components
- etc., etc.
The Subsumption Rule
- Defining a binary subtype relation between types.
- Enriching the typing relation to take subtyping into account.
Gamma ⊢ t1 ∈ T1 T1 <: T2 | (T_Sub) |
Gamma ⊢ t1 ∈ T2 |
The Subtype Relation
Structural Rules
S <: U U <: T | (S_Trans) |
S <: T |
(S_Refl) | |
T <: T |
Products
S1 <: T1 S2 <: T2 | (S_Prod) |
S1 * S2 <: T1 * T2 |
Arrows
f : C → Student
g : (C→Person) → D Is it safe to allow the application g f?
C→Student <: C→Person I.e., arrow is covariant in its right-hand argument.
Now suppose we have:
f : Person → C
g : (Student→C) → D Is it safe to allow the application g f?
Person → C <: Student → C I.e., arrow is contravariant in its left-hand argument.
Putting these together...
T1 <: S1 S2 <: T2 | (S_Arrow) |
S1 -> S2 <: T1 -> T2 |
Suppose we have S <: T and U <: V. Which of the following
subtyping assertions is false?
(1) S×U <: T×V
(2) T→U <: S→U
(3) (S→U) → (S×V) <: (S→U) → (T×U)
(4) (T×U) → V <: (S×U) → V
(5) S→U <: S→V
Suppose again that we have S <: T and U <: V. Which of the
following is incorrect?
(1) (T→T)*U <: (S→T)*V
(2) T→U <: S→V
(3) (S→U) → (S→V) <: (T→U) → (T→V)
(4) (S→V) → V <: (T→U) → V
(5) S → (V→U) <: S → (U→U)
Records
The basic intuition is that it is always safe to use a "bigger" record in place of a "smaller" one. That is, given a record type, adding extra fields will always result in a subtype. If some code is expecting a record with fields x and y, it is perfectly safe for it to receive a record with fields x, y, and z; the z field will simply be ignored. For example,
{name:String, age:Nat, gpa:Nat} <: {name:String, age:Nat}
{name:String, age:Nat} <: {name:String}
{name:String} <: {} This is known as "width subtyping" for records.
We can also create a subtype of a record type by replacing the type of one of its fields with a subtype. If some code is expecting a record with a field x of type T, it will be happy with a record having a field x of type S as long as S is a subtype of T. For example,
{x:Student} <: {x:Person} This is known as "depth subtyping".
Finally, although the fields of a record type are written in a particular order, the order does not really matter. For example,
{name:String,age:Nat} <: {age:Nat,name:String} This is known as "permutation subtyping".
We could formalize these requirements in a single subtyping rule for records as follows:
forall jk in j1..jn, | |
exists ip in i1..im, such that | |
jk=ip and Sp <: Tk | (S_Rcd) |
{i1:S1...im:Sm} <: {j1:T1...jn:Tn} |
First, adding fields to the end of a record type gives a subtype:
n > m | (S_RcdWidth) |
{i1:T1...in:Tn} <: {i1:T1...im:Tm} |
Second, subtyping can be applied inside the components of a compound record type:
S1 <: T1 ... Sn <: Tn | (S_RcdDepth) |
{i1:S1...in:Sn} <: {i1:T1...in:Tn} |
Third, subtyping can reorder fields. For example, we want {name:String, gpa:Nat, age:Nat} <: Person, but we haven't quite achieved this yet: using just S_RcdDepth and S_RcdWidth we can only drop fields from the end of a record type. So we add:
{i1:S1...in:Sn} is a permutation of {j1:T1...jn:Tn} | (S_RcdPerm) |
{i1:S1...in:Sn} <: {j1:T1...jn:Tn} |
It is worth noting that full-blown language designs may choose not to adopt all of these subtyping rules. For example, in Java:
- Each class member (field or method) can be assigned a single
index, adding new indices "on the right" as more members are
added in subclasses (i.e., no permutation for classes).
- A class may implement multiple interfaces -- so-called "multiple
inheritance" of interfaces (i.e., permutation is allowed for
interfaces).
- In early versions of Java, a subclass could not change the argument or result types of a method of its superclass (i.e., no depth subtyping or no arrow subtyping, depending how you look at it).
Top
(S_Top) | |
S <: Top |
Summary
- adding a base type Top,
- adding the rule of subsumption
to the typing relation, andGamma ⊢ t1 ∈ T1 T1 <: T2 (T_Sub) Gamma ⊢ t1 ∈ T2 - defining a subtype relation as follows:
S <: U U <: T (S_Trans) S <: T (S_Refl) T <: T (S_Top) S <: Top S1 <: T1 S2 <: T2 (S_Prod) S1 * S2 <: T1 * T2 T1 <: S1 S2 <: T2 (S_Arrow) S1 -> S2 <: T1 -> T2 n > m (S_RcdWidth) {i1:T1...in:Tn} <: {i1:T1...im:Tm} S1 <: T1 ... Sn <: Tn (S_RcdDepth) {i1:S1...in:Sn} <: {i1:T1...in:Tn} {i1:S1...in:Sn} is a permutation of {j1:T1...jn:Tn} (S_RcdPerm) {i1:S1...in:Sn} <: {j1:T1...jn:Tn}
Suppose we have S <: T and U <: V. Which of the following
subtyping assertions is false?
(1) S×U <: Top
(2) {i1:S,i2:T}->U <: {i1:S,i2:T,i3:V}->U
(3) (S→T) → (Top → Top) <: (S→T) → Top
(4) (Top → Top) → V <: Top → V
(5) S → {i1:U,i2:V} <: S → {i2:V,i1:U}
How about these?
(1) {i1:Top} <: Top
(2) Top → (Top → Top) <: Top → Top
(3) {i1:T} → {i1:T} <: {i1:T,i2:S} → Top
(4) {i1:T,i2:V,i3:V} <: {i1:S,i2:U} × {i3:V}
(5) Top → {i1:U,i2:V} <: {i1:S} → {i2:V,i1:V}
What is the smallest type T that makes the following
assertion true?
a:A ⊢ (\p:(A×T). (p.snd) (p.fst)) (a, \z:A.z) \in A
(1) Top
(2) A
(3) Top→Top
(4) Top→A
(5) A→A
(6) A→Top
a:A ⊢ (\p:(A×T). (p.snd) (p.fst)) (a, \z:A.z) \in A
What is the largest type T that makes the following
assertion true?
a:A ⊢ (\p:(A×T). (p.snd) (p.fst)) (a, \z:A.z) \in A
(1) Top
(2) A
(3) Top→Top
(4) Top→A
(5) A→A
(6) A→Top
a:A ⊢ (\p:(A×T). (p.snd) (p.fst)) (a, \z:A.z) \in A
"The type Bool has no proper subtypes." (I.e., the only
type smaller than Bool is Bool itself.)
(1) True
(2) False
"Suppose S, T1, and T2 are types with S <: T1 → T2. Then
S itself is an arrow type -- i.e., S = S1 → S2 for some S1
and S2 -- with T1 <: S1 and S2 <: T2."
(1) True
(2) False
Formal Definitions
Inductive ty : Type :=
| Ty_Top : ty
| Ty_Bool : ty
| Ty_Base : string → ty
| Ty_Arrow : ty → ty → ty
| Ty_Unit : ty
.
Inductive tm : Type :=
| tm_var : string → tm
| tm_app : tm → tm → tm
| tm_abs : string → ty → tm → tm
| tm_true : tm
| tm_false : tm
| tm_if : tm → tm → tm → tm
| tm_unit : tm
.
| Ty_Top : ty
| Ty_Bool : ty
| Ty_Base : string → ty
| Ty_Arrow : ty → ty → ty
| Ty_Unit : ty
.
Inductive tm : Type :=
| tm_var : string → tm
| tm_app : tm → tm → tm
| tm_abs : string → ty → tm → tm
| tm_true : tm
| tm_false : tm
| tm_if : tm → tm → tm → tm
| tm_unit : tm
.
Fixpoint subst (x : string) (s : tm) (t : tm) : tm :=
match t with
| tm_var y ⇒
if eqb_string x y then s else t
| <{\y:T, t1}> ⇒
if eqb_string x y then t else <{\y:T, [x:=s] t1}>
| <{t1 t2}> ⇒
<{([x:=s] t1) ([x:=s] t2)}>
| <{true}> ⇒
<{true}>
| <{false}> ⇒
<{false}>
| <{if t1 then t2 else t3}> ⇒
<{if ([x:=s] t1) then ([x:=s] t2) else ([x:=s] t3)}>
| <{unit}> ⇒
<{unit}>
end
where "'[' x ':=' s ']' t" := (subst x s t) (in custom stlc).
Inductive value : tm → Prop :=
| v_abs : ∀ x T2 t1,
value <{\x:T2, t1}>
| v_true :
value <{true}>
| v_false :
value <{false}>
| v_unit :
value <{unit}>
.
| v_abs : ∀ x T2 t1,
value <{\x:T2, t1}>
| v_true :
value <{true}>
| v_false :
value <{false}>
| v_unit :
value <{unit}>
.
Inductive step : tm → tm → Prop :=
| ST_AppAbs : ∀ x T2 t1 v2,
value v2 →
<{(\x:T2, t1) v2}> --> <{ [x:=v2]t1 }>
| ST_App1 : ∀ t1 t1' t2,
t1 --> t1' →
<{t1 t2}> --> <{t1' t2}>
| ST_App2 : ∀ v1 t2 t2',
value v1 →
t2 --> t2' →
<{v1 t2}> --> <{v1 t2'}>
| ST_IfTrue : ∀ t1 t2,
<{if true then t1 else t2}> --> t1
| ST_IfFalse : ∀ t1 t2,
<{if false then t1 else t2}> --> t2
| ST_If : ∀ t1 t1' t2 t3,
t1 --> t1' →
<{if t1 then t2 else t3}> --> <{if t1' then t2 else t3}>
where "t '-->' t'" := (step t t').
Inductive subtype : ty → ty → Prop :=
| S_Refl : ∀ T,
T <: T
| S_Trans : ∀ S U T,
S <: U →
U <: T →
S <: T
| S_Top : ∀ S,
S <: <{Top}>
| S_Arrow : ∀ S1 S2 T1 T2,
T1 <: S1 →
S2 <: T2 →
<{S1→S2}> <: <{T1→T2}>
where "T '<:' U" := (subtype T U).
Note that we don't need any special rules for base types (Bool
and Base): they are automatically subtypes of themselves (by
S_Refl) and Top (by S_Top), and that's all we want.
Inductive has_type : context → tm → ty → Prop :=
(* Same as before: *)
(* pure STLC *)
| T_Var : ∀ Gamma x T1,
Gamma x = Some T1 →
Gamma ⊢ x \in T1
| T_Abs : ∀ Gamma x T1 T2 t1,
(x ⊢> T2 ; Gamma) ⊢ t1 \in T1 →
Gamma ⊢ \x:T2, t1 \in (T2 → T1)
| T_App : ∀ T1 T2 Gamma t1 t2,
Gamma ⊢ t1 \in (T2 → T1) →
Gamma ⊢ t2 \in T2 →
Gamma ⊢ t1 t2 \in T1
| T_True : ∀ Gamma,
Gamma ⊢ true \in Bool
| T_False : ∀ Gamma,
Gamma ⊢ false \in Bool
| T_If : ∀ t1 t2 t3 T1 Gamma,
Gamma ⊢ t1 \in Bool →
Gamma ⊢ t2 \in T1 →
Gamma ⊢ t3 \in T1 →
Gamma ⊢ if t1 then t2 else t3 \in T1
| T_Unit : ∀ Gamma,
Gamma ⊢ unit \in Unit
(* New rule of subsumption: *)
| T_Sub : ∀ Gamma t1 T1 T2,
Gamma ⊢ t1 \in T1 →
T1 <: T2 →
Gamma ⊢ t1 \in T2
where "Gamma '⊢' t '∈' T" := (has_type Gamma t T).
(* Same as before: *)
(* pure STLC *)
| T_Var : ∀ Gamma x T1,
Gamma x = Some T1 →
Gamma ⊢ x \in T1
| T_Abs : ∀ Gamma x T1 T2 t1,
(x ⊢> T2 ; Gamma) ⊢ t1 \in T1 →
Gamma ⊢ \x:T2, t1 \in (T2 → T1)
| T_App : ∀ T1 T2 Gamma t1 t2,
Gamma ⊢ t1 \in (T2 → T1) →
Gamma ⊢ t2 \in T2 →
Gamma ⊢ t1 t2 \in T1
| T_True : ∀ Gamma,
Gamma ⊢ true \in Bool
| T_False : ∀ Gamma,
Gamma ⊢ false \in Bool
| T_If : ∀ t1 t2 t3 T1 Gamma,
Gamma ⊢ t1 \in Bool →
Gamma ⊢ t2 \in T1 →
Gamma ⊢ t3 \in T1 →
Gamma ⊢ if t1 then t2 else t3 \in T1
| T_Unit : ∀ Gamma,
Gamma ⊢ unit \in Unit
(* New rule of subsumption: *)
| T_Sub : ∀ Gamma t1 T1 T2,
Gamma ⊢ t1 \in T1 →
T1 <: T2 →
Gamma ⊢ t1 \in T2
where "Gamma '⊢' t '∈' T" := (has_type Gamma t T).
Properties
- Statements of these theorems don't need to change, compared
to pure STLC
- But proofs are a bit more involved, to account for the additional flexibility in the typing relation
Inversion Lemmas for Subtyping
- Bool is the only subtype of Bool, and
- every subtype of an arrow type is itself an arrow type.
Lemma sub_inversion_Bool : ∀ U,
U <: <{Bool}> →
U = <{Bool}>.
Lemma sub_inversion_arrow : ∀ U V1 V2,
U <: <{V1→V2}> →
∃ U1 U2,
U = <{U1→U2}> ∧ V1 <: U1 ∧ U2 <: V2.
U <: <{Bool}> →
U = <{Bool}>.
Proof with auto.
intros U Hs.
remember <{Bool}> as V.
(* FILL IN HERE *) Admitted.
☐
intros U Hs.
remember <{Bool}> as V.
(* FILL IN HERE *) Admitted.
☐
Lemma sub_inversion_arrow : ∀ U V1 V2,
U <: <{V1→V2}> →
∃ U1 U2,
U = <{U1→U2}> ∧ V1 <: U1 ∧ U2 <: V2.
Proof with eauto.
intros U V1 V2 Hs.
remember <{V1→V2}> as V.
generalize dependent V2. generalize dependent V1.
(* FILL IN HERE *) Admitted.
intros U V1 V2 Hs.
remember <{V1→V2}> as V.
generalize dependent V2. generalize dependent V1.
(* FILL IN HERE *) Admitted.
Canonical Forms
Lemma canonical_forms_of_arrow_types : ∀ Gamma s T1 T2,
Gamma ⊢ s \in (T1→T2) →
value s →
∃ x S1 s2,
s = <{\x:S1,s2}>.
Gamma ⊢ s \in (T1→T2) →
value s →
∃ x S1 s2,
s = <{\x:S1,s2}>.
Proof with eauto.
(* FILL IN HERE *) Admitted.
(* FILL IN HERE *) Admitted.
Similarly, the canonical forms of type Bool are the constants
tm_true and tm_false.
Lemma canonical_forms_of_Bool : ∀ Gamma s,
Gamma ⊢ s \in Bool →
value s →
s = tm_true ∨ s = tm_false.
Gamma ⊢ s \in Bool →
value s →
s = tm_true ∨ s = tm_false.
Proof with eauto.
intros Gamma s Hty Hv.
remember <{Bool}> as T.
induction Hty; try solve_by_invert...
- (* T_Sub *)
subst. apply sub_inversion_Bool in H. subst...
Qed.
intros Gamma s Hty Hv.
remember <{Bool}> as T.
induction Hty; try solve_by_invert...
- (* T_Sub *)
subst. apply sub_inversion_Bool in H. subst...
Qed.
Progress
- If the last step in the typing derivation uses rule T_App,
then there are terms t1 t2 and types T1 and T2 such that
t = t1 t2, T = T2, empty ⊢ t1 \in T1 → T2, and empty ⊢
t2 \in T1. Moreover, by the induction hypothesis, either t1 is
a value or it steps, and either t2 is a value or it steps.
There are three possibilities to consider:
- Suppose t1 --> t1' for some term t1'. Then t1 t2 --> t1' t2
by ST_App1.
- Suppose t1 is a value and t2 --> t2' for some term t2'.
Then t1 t2 --> t1 t2' by rule ST_App2 because t1 is a
value.
- Finally, suppose t1 and t2 are both values. By the
canonical forms lemma for arrow types, we know that t1 has the
form \x:S1.s2 for some x, S1, and s2. But then
(\x:S1.s2) t2 --> [x:=t2]s2 by ST_AppAbs, since t2 is a
value.
- Suppose t1 --> t1' for some term t1'. Then t1 t2 --> t1' t2
by ST_App1.
- If the final step of the derivation uses rule T_Test, then there
are terms t1, t2, and t3 such that t = tm_if t1 then t2 else
t3, with empty ⊢ t1 \in Bool and with empty ⊢ t2 \in T and
empty ⊢ t3 \in T. Moreover, by the induction hypothesis,
either t1 is a value or it steps.
- If t1 is a value, then by the canonical forms lemma for
booleans, either t1 = tm_true or t1 = tm_false. In either
case, t can step, using rule ST_TestTrue or ST_TestFalse.
- If t1 can step, then so can t, by rule ST_Test.
- If t1 is a value, then by the canonical forms lemma for
booleans, either t1 = tm_true or t1 = tm_false. In either
case, t can step, using rule ST_TestTrue or ST_TestFalse.
- If the final step of the derivation is by T_Sub, then there is a type T2 such that T1 <: T2 and empty ⊢ t1 \in T1. The desired result is exactly the induction hypothesis for the typing subderivation.
Theorem progress : ∀ t T,
empty ⊢ t \in T →
value t ∨ ∃ t', t --> t'.
empty ⊢ t \in T →
value t ∨ ∃ t', t --> t'.
Proof with eauto.
intros t T Ht.
remember empty as Gamma.
induction Ht; subst Gamma; auto.
- (* T_Var *)
discriminate.
- (* T_App *)
right.
destruct IHHt1; subst...
+ (* t1 is a value *)
destruct IHHt2; subst...
× (* t2 is a value *)
eapply canonical_forms_of_arrow_types in Ht1; [|assumption].
destruct Ht1 as [x [S1 [s2 H1]]]. subst.
∃ (<{ [x:=t2]s2 }>)...
× (* t2 steps *)
destruct H0 as [t2' Hstp]. ∃ <{ t1 t2' }>...
+ (* t1 steps *)
destruct H as [t1' Hstp]. ∃ <{ t1' t2 }>...
- (* T_Test *)
right.
destruct IHHt1.
+ (* t1 is a value *) eauto.
+ apply canonical_forms_of_Bool in Ht1; [|assumption].
destruct Ht1; subst...
+ destruct H. rename x into t1'. eauto.
Qed.
intros t T Ht.
remember empty as Gamma.
induction Ht; subst Gamma; auto.
- (* T_Var *)
discriminate.
- (* T_App *)
right.
destruct IHHt1; subst...
+ (* t1 is a value *)
destruct IHHt2; subst...
× (* t2 is a value *)
eapply canonical_forms_of_arrow_types in Ht1; [|assumption].
destruct Ht1 as [x [S1 [s2 H1]]]. subst.
∃ (<{ [x:=t2]s2 }>)...
× (* t2 steps *)
destruct H0 as [t2' Hstp]. ∃ <{ t1 t2' }>...
+ (* t1 steps *)
destruct H as [t1' Hstp]. ∃ <{ t1' t2 }>...
- (* T_Test *)
right.
destruct IHHt1.
+ (* t1 is a value *) eauto.
+ apply canonical_forms_of_Bool in Ht1; [|assumption].
destruct Ht1; subst...
+ destruct H. rename x into t1'. eauto.
Qed.
Inversion Lemmas for Typing
- If the last step of the derivation is a use of T_Abs then there is a type T12 such that T = S1 → T12 and x:S1; Gamma ⊢ t2 \in T12. Picking T12 for S2 gives us what we need: S1 → T12 <: S1 → T12 follows from S_Refl.
- If the last step of the derivation is a use of T_Sub then there is a type S such that S <: T and Gamma ⊢ \x:S1.t2 \in S. The IH for the typing subderivation tells us that there is some type S2 with S1 → S2 <: S and x:S1; Gamma ⊢ t2 \in S2. Picking type S2 gives us what we need, since S1 → S2 <: T then follows by S_Trans.
Formally:
Lemma typing_inversion_abs : ∀ Gamma x S1 t2 T,
Gamma ⊢ \x:S1,t2 \in T →
∃ S2,
<{S1→S2}> <: T
∧ (x ⊢> S1 ; Gamma) ⊢ t2 \in S2.
Lemma typing_inversion_var : ∀ Gamma (x:string) T,
Gamma ⊢ x \in T →
∃ S,
Gamma x = Some S ∧ S <: T.
Proof with eauto.
(* FILL IN HERE *) Admitted.
Lemma typing_inversion_app : ∀ Gamma t1 t2 T2,
Gamma ⊢ t1 t2 \in T2 →
∃ T1,
Gamma ⊢ t1 \in (T1→T2) ∧
Gamma ⊢ t2 \in T1.
Proof with eauto.
(* FILL IN HERE *) Admitted.
Gamma ⊢ \x:S1,t2 \in T →
∃ S2,
<{S1→S2}> <: T
∧ (x ⊢> S1 ; Gamma) ⊢ t2 \in S2.
Proof with eauto.
intros Gamma x S1 t2 T H.
remember <{\x:S1,t2}> as t.
induction H;
inversion Heqt; subst; intros; try solve_by_invert.
- (* T_Abs *)
∃ T1...
- (* T_Sub *)
destruct IHhas_type as [S2 [Hsub Hty]]...
Qed.
intros Gamma x S1 t2 T H.
remember <{\x:S1,t2}> as t.
induction H;
inversion Heqt; subst; intros; try solve_by_invert.
- (* T_Abs *)
∃ T1...
- (* T_Sub *)
destruct IHhas_type as [S2 [Hsub Hty]]...
Qed.
Lemma typing_inversion_var : ∀ Gamma (x:string) T,
Gamma ⊢ x \in T →
∃ S,
Gamma x = Some S ∧ S <: T.
Proof with eauto.
(* FILL IN HERE *) Admitted.
Lemma typing_inversion_app : ∀ Gamma t1 t2 T2,
Gamma ⊢ t1 t2 \in T2 →
∃ T1,
Gamma ⊢ t1 \in (T1→T2) ∧
Gamma ⊢ t2 \in T1.
Proof with eauto.
(* FILL IN HERE *) Admitted.
Lemma weakening : ∀ Gamma Gamma' t T,
inclusion Gamma Gamma' →
Gamma ⊢ t \in T →
Gamma' ⊢ t \in T.
Proof.
intros Gamma Gamma' t T H Ht.
generalize dependent Gamma'.
induction Ht; eauto using inclusion_update.
Qed.
Lemma weakening_empty : ∀ Gamma t T,
empty ⊢ t \in T →
Gamma ⊢ t \in T.
Proof.
intros Gamma t T.
eapply weakening.
discriminate.
Qed.
inclusion Gamma Gamma' →
Gamma ⊢ t \in T →
Gamma' ⊢ t \in T.
Proof.
intros Gamma Gamma' t T H Ht.
generalize dependent Gamma'.
induction Ht; eauto using inclusion_update.
Qed.
Lemma weakening_empty : ∀ Gamma t T,
empty ⊢ t \in T →
Gamma ⊢ t \in T.
Proof.
intros Gamma t T.
eapply weakening.
discriminate.
Qed.
Substitution
Lemma substitution_preserves_typing : ∀ Gamma x U t v T,
(x ⊢> U ; Gamma) ⊢ t \in T →
empty ⊢ v \in U →
Gamma ⊢ [x:=v]t \in T.
Proof.
(x ⊢> U ; Gamma) ⊢ t \in T →
empty ⊢ v \in U →
Gamma ⊢ [x:=v]t \in T.
Proof.
Proof.
intros Gamma x U t v T Ht Hv.
remember (x ⊢> U; Gamma) as Gamma'.
generalize dependent Gamma.
induction Ht; intros Gamma' G; simpl; eauto.
(* FILL IN HERE *) Admitted.
intros Gamma x U t v T Ht Hv.
remember (x ⊢> U; Gamma) as Gamma'.
generalize dependent Gamma.
induction Ht; intros Gamma' G; simpl; eauto.
(* FILL IN HERE *) Admitted.
Preservation
- If the final step of the derivation is by T_App, then there
are terms t1 and t2 and types T1 and T2 such that
t = t1 t2, T = T2, empty ⊢ t1 \in T1 → T2, and
empty ⊢ t2 \in T1.
- If the final step of the derivation uses rule T_Test, then
there are terms t1, t2, and t3 such that t = tm_if t1 then
t2 else t3, with empty ⊢ t1 \in Bool and with empty ⊢ t2
\in T and empty ⊢ t3 \in T. Moreover, by the induction
hypothesis, if t1 steps to t1' then empty ⊢ t1' : Bool.
There are three cases to consider, depending on which rule was
used to show t --> t'.
- If t --> t' by rule ST_Test, then t' = tm_if t1' then t2
else t3 with t1 --> t1'. By the induction hypothesis,
empty ⊢ t1' \in Bool, and so empty ⊢ t' \in T by
T_Test.
- If t --> t' by rule ST_TestTrue or ST_TestFalse, then
either t' = t2 or t' = t3, and empty ⊢ t' \in T
follows by assumption.
- If t --> t' by rule ST_Test, then t' = tm_if t1' then t2
else t3 with t1 --> t1'. By the induction hypothesis,
empty ⊢ t1' \in Bool, and so empty ⊢ t' \in T by
T_Test.
- If the final step of the derivation uses rule T_Test, then
there are terms t1, t2, and t3 such that t = tm_if t1 then
t2 else t3, with empty ⊢ t1 \in Bool and with empty ⊢ t2
\in T and empty ⊢ t3 \in T. Moreover, by the induction
hypothesis, if t1 steps to t1' then empty ⊢ t1' : Bool.
There are three cases to consider, depending on which rule was
used to show t --> t'.
- If the final step of the derivation is by T_Sub, then there is a type S such that S <: T and empty ⊢ t \in S. The result is immediate by the induction hypothesis for the typing subderivation and an application of T_Sub. ☐
Theorem preservation : ∀ t t' T,
empty ⊢ t \in T →
t --> t' →
empty ⊢ t' \in T.
empty ⊢ t \in T →
t --> t' →
empty ⊢ t' \in T.
Proof with eauto.
intros t t' T HT. generalize dependent t'.
remember empty as Gamma.
induction HT;
intros t' HE; subst;
try solve [inversion HE; subst; eauto].
- (* T_App *)
inversion HE; subst...
(* Most of the cases are immediate by induction,
and eauto takes care of them *)
+ (* ST_AppAbs *)
destruct (abs_arrow _ _ _ _ _ HT1) as [HA1 HA2].
apply substitution_preserves_typing with T0...
Qed.
intros t t' T HT. generalize dependent t'.
remember empty as Gamma.
induction HT;
intros t' HE; subst;
try solve [inversion HE; subst; eauto].
- (* T_App *)
inversion HE; subst...
(* Most of the cases are immediate by induction,
and eauto takes care of them *)
+ (* ST_AppAbs *)
destruct (abs_arrow _ _ _ _ _ HT1) as [HA1 HA2].
apply substitution_preserves_typing with T0...
Qed.