is not typable, since it applies a function that wants a two-field
record to an argument that actually provides three fields, while the
app rule demands that the domain type of the function being
applied must match the type of the argument precisely.
But this is silly: we're passing the function a better argument
than it needs! The only thing the body of the function can
possibly do with its record argument r is project the field age
from it: nothing else is allowed by the type, and the presence or
absence of an extra gpa field makes no difference at all. So,
intuitively, it seems that this function should be applicable to
any record value that has at least an age field.
More generally, a record with more fields is "at least as good in
any context" as one with just a subset of these fields, in the
sense that any value belonging to the longer record type can be
used safely in any context expecting the shorter record type. If
the context expects something with the shorter type but we actually
give it something with the longer type, nothing bad will
happen (formally, the program will not get stuck).
The principle at work here is called subtyping. We say that "σ
is a subtype of τ", written σ <: τ, if a value of type σ can
safely be used in any context where a value of type τ is
expected. The idea of subtyping applies not only to records, but
to all of the type constructors in the language -- functions,
pairs, etc.
Safe substitution principle:
σ is a subtype of τ, written σ <: τ, if a value of type
σ can safely be used in any context where a value of type
τ is expected.
Subtyping plays a fundamental role in many programming
languages -- in particular, it is central to the design of
object-oriented languages and their libraries.
An object in Java, C#, etc. can be thought of as a record,
some of whose fields are functions ("methods") and some of whose
fields are data values ("fields" or "instance variables").
Invoking a method m of an object o on some arguments a₁..an
roughly consists of projecting out the m field of o and
applying it to a₁..an.
The type of an object is called a class -- or, in some
languages, an interface. It describes which methods and which
data fields the object offers. Classes and interfaces are related
by the subclass and subinterface relations. An object
belonging to a subclass (or subinterface) is required to provide
all the methods and fields of one belonging to a superclass (or
superinterface), plus possibly some more.
The fact that an object from a subclass can be used in place of
one from a superclass provides a degree of flexibility that is
extremely handy for organizing complex libraries. For example, a
GUI toolkit like Java's Swing framework might define an abstract
interface Component that collects together the common fields and
methods of all objects having a graphical representation that can
be displayed on the screen and interact with the user, such as the
buttons, checkboxes, and scrollbars of a typical GUI. A method
that relies only on this common interface can now be applied to
any of these objects.
Of course, real object-oriented languages include many other
features besides these. For example, fields can be updated.
Fields and methods can be declared "private". Classes can give
initializers that are used when constructing objects. Code in
subclasses can cooperate with code in superclasses via
inheritance. Classes can have static methods and fields. Etc.,
etc.
To keep things simple here, we won't deal with any of these
issues -- in fact, we won't even talk any more about objects or
classes. (There is a lot of discussion in Pierce (2002)Benjamin C. Pierce (2002). “Types and Programming Languages”. MIT Press. ., if
you are interested.) Instead, we'll study the core concepts
behind the subclass / subinterface relation in the simplified
setting of the STLC.
This rule says, intuitively, that it is OK to "forget" some of
what we know about a term.
For example, we may know that t₁ is a record with two
fields (e.g., τ₁ = {x:α→α, y:β→β}, but choose to forget about
one of the fields (τ₂ = {y:β→β}) so that we can pass t₁ to a
function that requires just a single-field record.
To start off, we impose two "structural rules" that are
independent of any particular type constructor: a rule of
transitivity, which says intuitively that, if σ is
better (richer, safer) than υ and υ is better than τ,
then σ is better than τ...
σ <: υ υ <: τ
---------------- (trans)
σ <: τ
... and a rule of reflexivity, since certainly any type τ is
as good as itself:
Now we consider the individual type constructors, one by one,
beginning with product types. We consider one pair to be a subtype
of another if each of its components is.
The subtyping rule for arrows is a little less intuitive.
Suppose we have functions f and g with these types:
f : C → Student
g : (C→Person) → D
That is, f is a function that yields a record of type Student,
and g is a (higher-order) function that expects its argument to be
a function yielding a record of type Person. Also suppose that
Student is a subtype of Person. Then the application g f is
safe even though their types do not match up precisely, because
the only thing g can do with f is to apply it to some
argument (of type C); the result will actually be a Student,
while g will be expecting a Person, but this is safe because
the only thing g can then do is to project out the two fields
that it knows about (name and age), and these will certainly
be among the fields that are present.
This example suggests that the subtyping rule for arrow types
should say that two arrow types are in the subtype relation if
their results are:
But notice that the argument types are subtypes "the other way round":
in order to conclude that σ₁→σ₂ to be a subtype of τ₁→τ₂, it
must be the case that τ₁ is a subtype of σ₁. The arrow
constructor is said to be contravariant in its first argument
and covariant in its second.
Here is an example that illustrates this:
f : Person → C
g : (Student → C) → D
The application g f is safe, because the only thing the body of
g can do with f is to apply it to some argument of type
Student. Since f requires records having (at least) the
fields of a Person, this will always work. So Person → C is a
subtype of Student → C since Student is a subtype of
Person.
The intuition is that, if we have a function f of type σ₁→σ₂,
then we know that f accepts elements of type σ₁; clearly, f
will also accept elements of any subtype τ₁ of σ₁. The type of
f also tells us that it returns elements of type σ₂; we can
also view these results belonging to any supertype τ₂ of
σ₂. That is, any function f of type σ₁→σ₂ can also be
viewed as having type τ₁→τ₂.
Quiz
Suppose we have σ <: τ and υ <: δ. Which of the following
subtyping assertions is false?
(A) σ×υ <: τ×δ
(B) τ→υ <: σ→υ
(C) (σ→υ) → (σ×δ) <: (σ→υ) → (τ×υ)
(D) (τ×υ) → δ <: (σ×υ) → δ
(E) σ→υ <: σ→δ
Quiz
Suppose again that we have σ <: τ and υ <: δ. Which of the
following is incorrect?
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,
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 τ, it will be happy with a record
having a field x of type σ as long as σ is a subtype of
τ. 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:
∀ jk in j₁..jn,
∃ ip in i₁..im, such that
jk=ip and σp <: τk
---------------------------------- (rcd)
{i₁:σ₁...im:σm} <: {j₁:τ₁...jn:τn}
That is, the record on the left should have all the field labels of
the one on the right (and possibly more), while the types of the
common fields should be in the subtype relation.
However, this rule is rather heavy and hard to read, so it is often
decomposed into three simpler rules, which can be combined using
trans to achieve all the same effects.
First, adding fields to the end of a record type gives a subtype:
n > m
--------------------------------- (rcdWidth)
{i₁:τ₁...in:τn} <: {i₁:τ₁...im:τm}
We can use rcdWidth to drop later fields of a multi-field
record while keeping earlier fields, showing for example that
{age:Nat,name:String} <: {age:Nat}.
Second, subtyping can be applied inside the components of a compound
record type:
For example, we can use rcdDepth and rcdWidth together to
show that {y:Student, x:Nat} <: {y:Person}.
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 rcdDepth and
rcdWidth we can only drop fields from the end of a record
type. So we add:
{i₁:σ₁...in:σn} is a permutation of {j₁:τ₁...jn:τn}
--------------------------------------------------- (rcdPerm)
{i₁:σ₁...in:σn} <: {j₁:τ₁...jn:τn}
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).
Exercise★★(arrow_sub_wrong) (Manually graded)
Suppose we had incorrectly defined subtyping as covariant on both
the right and the left of arrow types:
Finally, it is convenient to give the subtype relation a maximum
element -- a type that lies above every other type and is
inhabited by all (well-typed) values. We do this by adding to the
language one new type constant, called ⊤ (pronounced "⊤" and written ⊤),
together with a subtyping rule that places it above every other type in the
subtype relation:
-------- (⊤)
σ <: ⊤
The ⊤ type is an analog of the Object type in Java and C#.
The following "thought exercises" are repeated later as formal
exercises.
Exercise★(subtype_instances_tf_1) (Optional)
Suppose we have types σ, τ, υ, and δ with σ <: τ
and υ <: δ. Which of the following subtyping assertions
are then true? Write true or false after each one.
(A, B, and C here are base types like Bool, Nat, etc.
τ→σ <: τ→σ
⊤→υ <: σ→⊤
(C→C) → (A*B) <: (C→C) → (⊤*B)
τ→τ→υ <: σ→σ→V
(τ→τ)→υ <: (σ→σ)→V
((τ→σ)→τ)→υ <: ((σ→τ)→σ)→V
σ*δ <: τ*υ
Exercise★(subtype_order) (Manually graded)
The following types happen to form a linear order with respect to subtyping:
⊤
⊤ → Student
Student → Person
Student → ⊤
Person → Student
Write these types in order from the most specific to the most general.
Where does the type ⊤→⊤→Student fit into this order?
That is, state how ⊤ → (⊤ → Student) compares with each
of the five types above. It may be unrelated to some of them.
Which of the following statements are true, and which are false?
There exists a type that is a supertype of every other type.
There exists a type that is a subtype of every other type.
There exists a pair type that is a supertype of every other
pair type.
There exists a pair type that is a subtype of every other
pair type.
There exists an arrow type that is a supertype of every other
arrow type.
There exists an arrow type that is a subtype of every other
arrow type.
There is an infinite descending chain of distinct types in the
subtype relation---that is, an infinite sequence of types
σ₀, σ₁, etc., such that all the σi's are different and
each σ(i+1) is a subtype of σi.
There is an infinite ascending chain of distinct types in
the subtype relation---that is, an infinite sequence of types
σ₀, σ₁, etc., such that all the σi's are different and
each σ(i+1) is a supertype of σi.
Exercise★(proper_subtypes) (Manually graded)
Is the following statement true or false? Briefly explain your
answer. (A here and below represents an arbitrary base type.)
What is the smallest type τ ("smallest" in the subtype
relation) that makes the following assertion true? (Assume we
have Unit among the base types and unit as a constant of this
type. )
∅ ⊢ (λp:τ×⊤. p.fst) ((λz:A,z). unit) ⦂ A→A
What is the largest type τ that makes the same assertion true?
Exercise★★(small_large_2) (Manually graded)
What is the smallest type τ that makes the following
assertion true?
∅ ⊢ (λp:(A→A × B→B), p) ((λz:A.z), (λz:B.z)) ⦂ τ
What is the largest type τ that makes the same assertion true?
Exercise★★(small_large_3) (Optional)
What is the smallest type τ that makes the following
assertion true?
a:A ⊢ (λp:(A×τ). (p.snd) (p.fst)) (a. λz:A.z) ⦂ A
What is the largest type τ that makes the same assertion true?
Quiz
What is the smallest type τ that makes the following
assertion true?
a:A ⊢ (λp:(A×τ). (p.snd) (p.fst)) (a, λz:A. z) ⦂ A
(A) ⊤
(B) A
(C) ⊤→⊤
(D) ⊤→A
(E) A→A
(F) A→⊤
Quiz
What is the largest type τ that makes the following
assertion true?
a:A ⊢ (λp:(A×τ). (p.snd) (p.fst)) (a, λz:A.z) ⦂ A
(A) ⊤
(B) A
(C) ⊤→⊤
(D) ⊤→A
(E) A→A
(F) A→⊤
Quiz
"The type Bool has no proper subtypes." (I.e., the only
type smaller than Bool is Bool itself.)
(A) True
(B) False
Quiz
"Suppose σ, τ₁, and τ₂ are types with σ <: τ₁ → τ₂. Then
σ itself is an arrow type -- i.e., σ = σ₁ → σ₂ for some σ₁
and σ₂ -- with τ₁ <: σ₁ and σ₂ <: τ₂."
(A) True
(B) False
Exercise★★(small_large_4) (Manually graded)
What is the smallest type τ (if one exists) that makes the
following assertion true?
∃ σ,
∅ ⊢ (λp:(A*τ), (p.snd) (p.fst)) ⦂ σ
What is the largest type τ that makes the same
assertion true?
Exercise★★(smallest_1) (Manually graded)
What is the smallest type τ (if one exists) that makes
the following assertion true?
exists σ t,
∅ ⊢ (\x:τ, x x) t ⦂ σ
Exercise★★(smallest_2) (Manually graded)
What is the smallest type τ that makes the following
assertion true?
∅ ⊢ (\x:⊤, x) ((λz:A,z) , (λz:B,z)) ⦂ τ
Exercise★★★(count_supertypes) (Optional)
How many supertypes does the record type {x:A, y:C→C} have? That is,
how many different types τ are there such that {x:A, y:C→C} <: τ?
(We consider two types to be different if they are written
differently, even if each is a subtype of the other. For example,
{x:A,y:B} and {y:B,x:A} are different.)
Most of the definitions needed to formalize what we've discussed
above -- in particular, the syntax and operational semantics of
the language -- are identical to what we saw in the last chapter.
We just need to extend the typing relation with the subsumption
rule and add a new inductive definition for the subtyping
relation. Let's first do the identical bits.
We include products in the syntax of types and terms, but not,
for the moment, anywhere else; the products exercise below will
ask you to extend the definitions of the value relation, operational
semantics, subtyping relation, and typing relation and to extend
the proofs of progress and preservation to fully support products.
In the rest of the chapter, we formalize just base types,
booleans, arrow types, Unit, and ⊤, omitting record types
and leaving product types as an exercise. For the sake of more
interesting examples, we'll add an arbitrary set of base types
like String, Float, etc. (Since they are just for examples,
we won't bother adding any operations over these base types, but
we could easily do so.)
inductiveTy:Typewhere|top:Ty|bool:Ty|base:String→Ty|arrow:Ty→Ty→Ty|unit:Ty|prod:Ty→Ty→TyinductiveTm:Typewhere|var:String→Tm|app:Tm→Tm→Tm|abs:String→Ty→Tm→Tm|tru:Tm|fls:Tm|ite:Tm→Tm→Tm→Tm|unit:Tm|pair:Tm→Tm→Tm|fst:Tm→Tm|snd:Tm→TmNotationsyntax:50stlcTy:51" × "stlcTy:50:stlcTysyntax:50stlcTy:51" + "stlcTy:50:stlcTysyntax:max" ⊤ ":stlcTysyntax:51" [ "stlcTy:50" ] ":stlcTyopenLeaninscopedmacro_rules(kind:=Stlc.tyBracket)|`(<{~$τ:term}>)=>pureτ|`(<{($τ:stlcTy)}>)=>`(<{$τ:stlcTy}>)|`(<{⊤}>)=>`(Ty.top)|`(<{$x:ident}>)=>matchx.getId.toStringwith|"Bool"=>`(Ty.bool)|"Unit"=>`(Ty.unit)|_=>`(Ty.base$(quotex.getId.toString))|`(<{$τ₁:stlcTy→$τ₂:stlcTy}>)=>`(Ty.arrow<{$τ₁:stlcTy}><{$τ₂:stlcTy}>)|`(<{$τ₁:stlcTy×$τ₂:stlcTy}>)=>`(Ty.prod<{$τ₁:stlcTy}><{$τ₂:stlcTy}>)|`(<{$τ₁:stlcTy->$τ₂:stlcTy}>)=>`(Ty.arrow<{$τ₁:stlcTy}><{$τ₂:stlcTy}>)Ty.top.prodTy.top : Ty#check<{⊤×⊤}>Ty.bool.arrowTy.top : Ty#check<{Bool→⊤}>(Ty.bool.prodTy.unit).arrow(Ty.base"Nat") : Ty#check<{(Bool×Unit)->Nat}>scopedsyntax:50"if "stlcTm:51" then "stlcTm:50" else "stlcTm:50:stlcTmscopedsyntax:max" ( "stlcTm:60" , "stlcTm:60" ) ":stlcTmopenLeaninscopedmacro_rules(kind:=Stlc.tmBracket)|`(<{~$e:term}>)=>puree|`(<{($t:stlcTm)}>)=>`(<{$t:stlcTm}>)|`(<{$x:ident}>)=>matchx.getId.toStringwith|"Nat"=>Macro.throwErrorAtx"`Nat` is a type, not a term"|"Unit"=>Macro.throwErrorAtx"`Unit` is a type, not a term"|"fst"=>Macro.throwErrorAtx"`fst` must be applied to an argument"|"snd"=>Macro.throwErrorAtx"`snd` must be applied to an argument"|"unit"=>`(Tm.unit)|"true"=>`(Tm.tru)|"false"=>`(Tm.fls)|_=>`(Tm.var$(quotex.getId.toString))|`(<{λ$x:$τ.$t}>)=>do`(Tm.abs$(←Stlc.varStrx)<{$τ:stlcTy}><{$t:stlcTm}>)|`(<{$t₁:stlcTm$t₂:stlcTm}>)=>matcht₁with|`(stlcTm|$f:ident)=>matchf.getId.toStringwith|"fst"=>`(Tm.fst<{$t₂:stlcTm}>)|"snd"=>`(Tm.snd<{$t₂:stlcTm}>)|_=>`(Tm.app<{$t₁:stlcTm}><{$t₂:stlcTm}>)|_=>`(Tm.app<{$t₁:stlcTm}><{$t₂:stlcTm}>)|`(<{if$cthen$telse$e}>)=>`(Tm.ite<{$c:stlcTm}><{$t:stlcTm}><{$e:stlcTm}>)|`(<{($t₁:stlcTm,$t₂:stlcTm)}>)=>`(Tm.pair<{$t₁:stlcTm}><{$t₂:stlcTm}>)openLeanin/-- Is `s` usable as a bare variable in `stlcTm` rather than as reserved syntax? -/defisPlainTmVarName(s:String):Bool:=Stlc.isPlainNames&&s!="Bool"&&s!="unit"&&s!="Unit"&&s!="if"openLeanPrettyPrinterDelaboratorSubExprin/-- Rebuild `stlcTy` concrete syntax from a `Ty` value. -/partialdefdelabTyInner:DelabM(TSyntax`stlcTy):=doletstx←match_expr←getExprwith|Ty.bool=>`(stlcTy|$(mkIdent`Bool):ident)|Ty.unit=>`(stlcTy|$(mkIdent`Unit):ident)|Ty.top=>`(stlcTy|⊤)|Ty.arrow__=>doleta←withAppFn<|withAppArgdelabTyInnerletb←withAppArgdelabTyInner`(stlcTy|$a→$b)|Ty.prod__=>doleta←withAppFn<|withAppArgdelabTyInnerletb←withAppArgdelabTyInner`(stlcTy|$a×$b)|Ty.base_=>doletb←withAppArgdelab`(stlcTy|~($b))|_=>domatch←delabwith|`($i:ident)=>`(stlcTy|$i:ident)|e=>`(stlcTy|~$e)(⟨·⟩)<$>annotateTermInfo⟨stx.raw⟩openLeanPrettyPrinterDelaboratorSubExprin/-- Rebuild `stlcTm` concrete syntax from a `Tm` value. -/partialdefdelabTmInner:DelabM(TSyntax`stlcTm):=doletstx←match_expr←getExprwith|Tm.var_=>doletx←withAppArgdelabmatchxwith|`($s:str)=>ifisPlainTmVarNames.getStringthen`(stlcTm|$(mkIdent(Name.mkSimples.getString)):ident)elseletvar:Term:=mkIdent``Tm.var`(stlcTm|~($var$x))|_=>letvar:Term:=mkIdent``Tm.var`(stlcTm|~($var$x))|Tm.app__=>doletf←withAppFn<|withAppArgdelabTmInnerleta←withAppArgdelabTmInner`(stlcTm|$f$a)|Tm.abs___=>doletx←withAppFn<|withAppFn<|withAppArgStlc.delabVarInnerletτ←withAppFn<|withAppArgdelabTyInnerlett←withAppArgdelabTmInner`(stlcTm|λ$x:$τ.$t)|Tm.ite___=>doletc←withAppFn<|withAppFn<|withAppArgdelabTmInnerlett←withAppFn<|withAppArgdelabTmInnerlete←withAppArgdelabTmInner`(stlcTm|if$cthen$telse$e)|Tm.pair__=>doleta←withAppFn<|withAppArgdelabTmInnerletb←withAppArgdelabTmInner`(stlcTm|($a,$b))|Tm.fst_=>doletb←withAppArgdelabTmInner`(stlcTm|$(mkIdent`fst):ident$b)|Tm.snd_=>doletb←withAppArgdelabTmInner`(stlcTm|$(mkIdent`snd):ident$b)|Tm.unit=>do`(stlcTm|$(mkIdent`unit):ident)|Tm.tru=>do`(stlcTm|$(mkIdent`true):ident)|Tm.fls=>do`(stlcTm|$(mkIdent`false):ident)|_=>do-- `subst` is defined below, so it is matched by name rather than with-- `match_expr`; a substitution prints in its own bracket notation.lete←getExprife.getAppFn.constName?==some`SltcExtended.subst&&e.getAppNumArgs==3thenletx←withAppFn<|withAppFn<|withAppArgStlc.delabVarInnerlets←withAppFn<|withAppArgdelabTmInnerlett←withAppArgdelabTmInner`(stlcTm|[$x:=$s]$t)elsematch←delabwith|`($i:ident)=>`(stlcTm|$i:ident)|e=>`(stlcTm|~$e)(⟨·⟩)<$>annotateTermInfo⟨stx.raw⟩openLeanPrettyPrinterDelaboratorSubExprin@[delabapp.StlcSub.Ty.bool,delabapp.StlcSub.Ty.arrow,delabapp.StlcSub.Ty.unit,delabapp.StlcSub.Ty.prod,delabapp.StlcSub.Ty.base,delabapp.StlcSub.Ty.top]defdelabTy:Delab:=whenPPOptiongetPPNotationdoguard<|match_expr←getExprwith|Ty.bool=>true|Ty.arrow__=>true|Ty.prod__=>true|Ty.base_=>true|Ty.top=>true|Ty.unit=>true|_=>falsematch←delabTyInnerwith|`(stlcTy|~$e)=>puree|e=>`(<{$e:stlcTy}>)openLeanPrettyPrinterDelaboratorSubExprin@[delabapp.StlcSub.Tm.var,delabapp.StlcSub.Tm.app,delabapp.StlcSub.Tm.abs,delabapp.StlcSub.Tm.ite,delabapp.StlcSub.Tm.pair,delabapp.StlcSub.Tm.fst,delabapp.StlcSub.Tm.snd,delabapp.StlcSub.Tm.unit,delabapp.StlcSub.Tm.tru,delabapp.StlcSub.Tm.fls]defdelabTm:Delab:=whenPPOptiongetPPNotationdoguard<|match_expr←getExprwith|Tm.var_=>true|Tm.app__=>true|Tm.abs___=>true|Tm.ite___=>true|Tm.unit=>true|Tm.tru=>true|Tm.fls=>true|Tm.pair__=>true|Tm.fst_=>true|Tm.snd_=>true|_=>falsematch←delabTmInnerwith|`(stlcTm|~($e))=>puree|`(stlcTm|~$e)=>puree|e=>`(<{$e:stlcTm}>)
Checks that the extended grammar parses the way it should.
The definition of substitution remains exactly the same as for the
pure STLC.
sectionset_optionhygienefalseinlocalmacro_rules(kind:=Stlc.tmBracket)|`(<{[$x:=$s]$t}>)=>do`(subst$(←Stlc.varStrx)<{$s:stlcTm}><{$t:stlcTm}>)defdeclaration uses `sorry`declaration uses `sorry`declaration uses `sorry`subst(x:String)(s:Tm)(t:Tm):Tm:=matchtwith-- pure STLC|.vary=>ifx=ythenselset|<{λ~y:~τ.~t₁}>=>ifx=ythentelse<{λ~y:~τ.[~x:=~s]~t₁}>|<{~t₁~t₂}>=><{([~x:=~s]~t₁)([~x:=~s]~t₂)}>-- unit|.unit=><{unit}>-- bools|<{true}>=><{true}>|<{false}>=><{false}>|<{if~t₁then~t₂else~t₃}>=><{if[~x:=~s]~t₁then[~x:=~s]~t₂else[~x:=~s]~t₃}>-- Complete the following cases when you do the `products` exercise later|<{(~t₁,~t₂)}>=>sorry|Tm.fstt=>sorry|Tm.sndt=>sorryendmacro_rules(kind:=Stlc.tmBracket)|`(<{[$x:=$s]$t}>)=>do`(subst$(←Stlc.varStrx)<{$s:stlcTm}><{$t:stlcTm}>)
inductiveTm.IsValue:Tm→Propwhere|abs:∀xτ₂t₁,IsValue<{λ~x:~τ₂.~t₁}>|tru:IsValue<{true}>|fls:IsValue<{false}>|unit:IsValue.unit-- Fill in more rules when you do the `products` exercise later-- FILL IN HEREattribute[StlcSubEval]Tm.IsValue.absTm.IsValue.truTm.IsValue.flsTm.IsValue.unitsectionset_optionhygienefalseinlocalnotation:40t:41" ⟶ "t':41=>Steptt'inductiveStep:Tm→Tm→Propwhere-- pure STLC|appAbs(x:String)(τ₂:Ty)(t₁v₂:Tm):v₂.IsValue→<{(λ~x:~τ₂.~t₁)~v₂}>⟶<{[~x:=~v₂]~t₁}>|app₁(t₁t₁'t₂:Tm):t₁⟶t₁'→<{~t₁~t₂}>⟶<{~t₁'~t₂}>|app₂(v₁t₂t₂':Tm):v₁.IsValue→t₂⟶t₂'→<{~v₁~t₂}>⟶<{~v₁~t₂'}>-- booleans|ifStep(t₁t₁'t₂t₃:Tm)(h:t₁⟶t₁'):<{if~t₁then~t₂else~t₃}>⟶<{if~t₁'then~t₂else~t₃}>|ifTrue(t₂t₃:Tm):<{iftruethen~t₂else~t₃}>⟶t₂|ifFalse(t₂t₃:Tm):<{iffalsethen~t₂else~t₃}>⟶t₃-- Fill in more rules when you do the `products` exercise later-- FILL IN HEREendscopednotation:40t:41" ⟶ "t':41=>Steptt'scopednotation:40t:41" ⟶* "t':41=>MultiSteptt'-- Be sure to add your constructors for pairs to this list laterattribute[StlcSubEval]Step.appAbsStep.app₁Step.app₂Step.ifStepStep.ifTrueStep.ifFalse-- FILL IN HERE
Now we come to the interesting part. We begin by defining
the subtyping relation and developing some of its important
technical properties.
The definition of subtyping is just what we sketched in the
motivating discussion.
sectionset_optionhygienefalseinlocalnotation:40τ:41" <: "τ':41=>Subtypeττ'inductiveSubtype:Ty→Ty→Propwhere|refl{τ:Ty}:τ<:τ|trans{συτ:Ty}(h₁:σ<:υ)(h₂:υ<:τ):σ<:τ|top{σ:Ty}:σ<:<{⊤}>|arrow{σ₁σ₂τ₁τ₂:Ty}(h₁:τ₁<:σ₁)(h₂:σ₂<:τ₂):<{~σ₁→~σ₂}><:<{~τ₁→~τ₂}>-- Fill in more rules when you do the `products` exercise later-- FILL IN HEREendscopednotation:40τ:41" <: "τ':41=>Subtypeττ'attribute[StlcSubTyping]Subtype.reflSubtype.transSubtype.topSubtype.arrow-- FILL IN HERE
Note that we don't need any special rules for base types (Bool
and Base): they are automatically subtypes of themselves (by
refl) and ⊤ (by top), and that's all we want.
Note that, because the Subtype rules are not "syntax directed"
(e.g., given a goal of the form ⊤ <: ⊤, you could apply the top rule,
the refl rule, the trans rule), we have to use solve_by_elim here
instead of apply_rules.
Exercise★★(subtyping_judgements) (Optional)
Leave this exercise until after you have finished adding product
types to the language - see exercise products - at least up to
this point in the file.
Recall that, in chapter MoreStlc, the optional section
"Encoding Records" describes how records can be encoded as pairs.
Using this encoding, define pair types representing the following
record types:
Person := { name : String }
Student := { name : String ; gpa : Float }
Employee := { name : String ; ssn : Integer }
The following facts are mostly easy to prove in Lean. To get
full benefit from the exercises, make sure you also
understand how to prove them on paper!
The only change to the typing relation is the addition of the rule
of subsumption, sub.
abbrevContext:=PartialMapStringTyNotation encoding: contexts and judgments
The context grammar stlcCtx is reused as well; only the map it denotes is new,
since the types it stores are this language's. As with subst, the judgment
rule is introduced twice: local and hygiene-free while the relation is being
declared, then again for real.
openLeanin/-- The `Context` denoted by a context expression. -/partialdefctxTerm(G:TSyntax`stlcCtx):MacroMTerm:=matchGwith|`(stlcCtx|∅)=>`((∅:Context))|`(stlcCtx|~$e)=>puree|`(stlcCtx|$x:stlcVar↦$τ:stlcTy;$G:stlcCtx)=>do`(PartialMap.update$(←ctxTermG)$(←Stlc.varStrx)<{$τ:stlcTy}>)|_=>Macro.throwUnsupportedsectionStlcExtendedset_optionhygienefalseinlocalmacro_rules(kind:=Stlc.judgeBracket)|`(<{$G:stlcCtx⊢$t:stlcTm⦂$τ:stlcTy}>)=>do`(HasType$(←ctxTermG)<{$t:stlcTm}><{$τ:stlcTy}>)inductiveHasType:Context→Tm→Ty→Propwhere-- pure STLC|var(Γ:Context)(x:String)(τ₁:Ty)(h:Γ[x]=someτ₁):<{~Γ⊢~(Tm.varx)⦂~τ₁}>|abs(Γ:Context)(x:String)(τ₁τ₂:Ty)(t₁:Tm)(h:<{~x↦~τ₂;~Γ⊢~t₁⦂~τ₁}>):<{~Γ⊢λ~x:~τ₂.~t₁⦂~τ₂→~τ₁}>|app(Γ:Context)(τ₁τ₂:Ty)(t₁t₂:Tm)(h₁:<{~Γ⊢~t₁⦂~τ₂→~τ₁}>)(h₂:<{~Γ⊢~t₂⦂~τ₂}>):<{~Γ⊢~t₁~t₂⦂~τ₁}>-- booleans|tru(Γ:Context):<{~Γ⊢true⦂Bool}>|fls(Γ:Context):<{~Γ⊢false⦂Bool}>|ite(Γ:Context)(t₁t₂t₃:Tm)(τ:Ty)(h₁:<{~Γ⊢~t₁⦂Bool}>)(h₂:<{~Γ⊢~t₂⦂~τ}>)(h₃:<{~Γ⊢~t₃⦂~τ}>):<{~Γ⊢if~t₁then~t₂else~t₃⦂~τ}>-- unit|unit(Γ:Context):<{~Γ⊢unit⦂Unit}>-- subsumption|sub(Γ:Context)(t₁:Tm)(τ₁τ₂:Ty)(ht:<{~Γ⊢~t₁⦂~τ₁}>)(hs:τ₁<:τ₂):<{~Γ⊢~t₁⦂~τ₂}>-- Fill in more rules when you do the `products` exercise later-- FILL IN HERE-- Make sure to add your constructors hereattribute[StlcSubTyping]HasType.varHasType.absHasType.appHasType.iteHasType.truHasType.flsHasType.unit-- FILL IN HERE
We deliberately exclude HasType.sub from the list of constructors with the
StlcSubTyping. apply_rules using StlcSubTyping will search for derivations
without using the subtyping rule; if you want to make use of it in a derivation you will
need to do so yourself.
Notation encoding: the judgment, for real
Closing the section retires the hygiene-free rule; the same rule is then
declared again, hygienically, for every later use, and a pair of unexpanders
prints judgments back in their own notation.
endStlcExtendedscopedmacro_rules(kind:=Stlc.judgeBracket)|`(<{$G:stlcCtx⊢$t:stlcTm⦂$τ:stlcTy}>)=>do`(HasType$(←ctxTermG)<{$t:stlcTm}><{$τ:stlcTy}>)openLeanPrettyPrinterin/-- Rebuild `stlcCtx` syntax from the term syntax of a `Context`, so that a
context prints as `x ↦ Nat ; Γ` rather than as a chain of map updates. -/partialdefunexpandCtx:Term→UnexpandM(TSyntax`stlcCtx)|`(∅)=>`(stlcCtx|∅)|`($x:str→ₚ$τ)=>dounexpandCtx(←`($x→ₚ$τ;∅))|`($x:str→ₚ$τ;$G)=>doletG'←unexpandCtxGletx':TSyntax`stlcVar←ifStlc.isPlainNamex.getStringthen`(stlcVar|$(mkIdent(Name.mkSimplex.getString)):ident)else`(stlcVar|~$x)matchτwith|`(<{$T':stlcTy}>)=>`(stlcCtx|$x':stlcVar↦$T';$G')|_=>`(stlcCtx|$x':stlcVar↦~($τ);$G')|G=>`(stlcCtx|~($G))openLeanPrettyPrinterin@[app_unexpanderHasType]defHasType.unexpand:Unexpander|`($_$G<{$t:stlcTm}><{$τ:stlcTy}>)=>do`(<{$(←unexpandCtxG)⊢$t⦂$τ}>)|`($_$G<{$t:stlcTm}>$τ)=>do`(<{$(←unexpandCtxG)⊢$t⦂~($τ)}>)|`($_$G$t<{$τ:stlcTy}>)=>do`(<{$(←unexpandCtxG)⊢~($t)⦂$τ}>)|`($_$G$t$τ)=>do`(<{$(←unexpandCtxG)⊢~($t)⦂~($τ)}>)|_=>throw()namespaceExamples
Do the following exercises after you have added product types to
the language. For each informal typing judgement, write it as a
formal statement in Lean and prove it.
The fundamental properties of the system that we want to
check are the same as always: progress and preservation.
However, their proofs do become a little bit more involved.
Before we look at the properties of the typing relation, we need
to establish a couple of critical structural properties of the
subtype relation:
Bool is the only subtype of Bool, and
every subtype of an arrow type is itself an arrow type.
These are called inversion lemmas because they play a
similar role in proofs as the inversion tactic: given a
hypothesis that there exists a derivation of some subtyping
statement σ <: τ and some constraints on the shape of σ and/or
τ, each inversion lemma reasons about what this derivation must
look like to tell us something further about the shapes of σ and
τ and the existence of subtype relations between their parts.
The proof of the progress theorem -- that a well-typed
non-value can always take a step -- doesn't need to change too
much: we just need one small refinement. When we're considering
the case where the term in question is an application t₁ t₂
where both t₁ and t₂ are values, we need to know that t₁ has
the form of a lambda-abstraction, so that we can apply the
abs reduction rule. In the ordinary STLC, this is
obvious: we know that t₁ has a function type τ₁₁→τ₁₂, and
there is only one rule that can be used to give a function type to
a value - rule abs - and the form of the conclusion of this
rule forces t₁ to be an abstraction.
In the STLC with subtyping, this reasoning doesn't quite work
because there's another rule that can be used to show that a value
has a function type: subsumption. Fortunately, this possibility
doesn't change things much: if the last rule used to show Γ ⊢ t₁ ⦂ τ₁₁→τ₁₂ is subsumption,
then there is some sub-derivation whose subject is also t₁, and we can reason by
induction until we finally bottom out at a use of abs.
This bit of reasoning is packaged up in the following lemma, which
tells us the possible "canonical forms" (i.e., values) of function
type.
The proof of progress now proceeds just like the one for the
pure STLC, except that in several places we invoke canonical forms
lemmas...
Theorem (Progress): For any term t and type τ, if ∅ ⊢ t ⦂ τ then t is a value or
t ⟶ t' for some term t'.
Proof: Let t and τ be given, with ∅ ⊢ t ⦂ τ.
Proceed by induction on the typing derivation.
The cases for abs, unit, tru and fls are
immediate because abstractions, unit, true, and
false are already values. The var case is vacuous
because variables cannot be typed in the empty context. The
remaining cases are more interesting:
If the last step in the typing derivation uses rule app,
then there are terms t₁t₂ and types τ₁ and τ₂ such that
t = t₁ t₂, τ = τ₂, ∅ ⊢ t₁ ⦂ τ₁ → τ₂, and ∅ ⊢ t₂ ⦂ τ₁.
Moreover, by the induction hypothesis, either
t₁ is a value or it steps, and either t₂ is a value or it
steps. There are three possibilities to consider:
First, suppose t₁ ⟶ t₁' for some term t₁'. Then t₁ t₂ ⟶ t₁' t₂ by app₁'.
Second, suppose t₁ is a value and t₂ ⟶ t₂' for some term
t₂'. Then t₁ t₂ ⟶ t₁ t₂' by rule app₂ because t₁
is a value.
Third, suppose t₁ and t₂ are both values. By the
canonical forms lemma for arrow types, we know that t₁ has
the form λ x : σ₁ . t₂ for some x, σ₁, and s₂. But then
(λ x : σ₁ . s₂) t₂ ⟶ [x := t₂] s₂ by appAbs, since t₂ is a
value.
If the final step of the derivation uses rule if, then
there are terms t₁, t₂, and t₃ such that t = if t₁ then t₂ else t₃,
with ∅ ⊢ t₁ ⦂ Bool and with ∅ ⊢ t₂ ⦂ τ and ∅ ⊢ t₃ ⦂ τ. Moreover, by the
induction hypothesis, either t₁ is a value or it steps.
If t₁ is a value, then by the canonical forms lemma for
booleans, either t₁ = true or t₁ = false. In
either case, t can step, using rule ifTrue or
ifFalse.
If t₁ can step, then so can t, by rule if.
If the final step of the derivation is by sub, then there is
a type τ₂ such that τ₁ <: τ₂ and ∅ ⊢ t₁ ⦂ τ₁. The
desired result is exactly the induction hypothesis for the
typing subderivation.
Formally:
theoremprogress(t:Tm)(τ:Ty)(h:<{∅⊢~t⦂~τ}>):t.IsValue∨∃t',t⟶t':=t:Tmτ:Tyh:<{∅⊢~(t)⦂~(τ)}>⊢ t.IsValue∨∃t',t⟶t't:Tmτ:TyΓ:Contextheq:∅=Γh:<{~(Γ)⊢~(t)⦂~(τ)}>⊢ t.IsValue∨∃t',t⟶t'Alternative `snd` has not been providedAlternative `pair` has not been providedAlternative `fst` has not been providedinductionhwith(t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∅=∅→t✝.IsValue∨∃t',t✝⟶t'⊢ <{sndt✝}>.IsValue∨∃t',<{sndt✝}>⟶t';first|t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∅=∅→t✝.IsValue∨∃t',t✝⟶t'⊢ <{sndt✝}>.IsValue∨∃t',<{sndt✝}>⟶t'-- discharge cases where `t` is obviously a value|try(t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∅=∅→t✝.IsValue∨∃t',t✝⟶t'⊢ <{sndt✝}>.IsValue;t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∅=∅→t✝.IsValue∨∃t',t✝⟶t'⊢ <{sndt✝}>.IsValue;All goals completed! 🐙))t:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'⊢ <{t₁t₂}>.IsValue∨∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'⊢ ∃t',<{t₁t₂}>⟶t';t:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'h✝:t₁.IsValue⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'h✝:∃t',t₁⟶t'⊢ ∃t',<{t₁t₂}>⟶t'-- t₁ is a valuecase_ht₁t:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValue⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueh✝:t₂.IsValue⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueh✝:∃t',t₂⟶t'⊢ ∃t',<{t₁t₂}>⟶t'-- t₂ is a valuecase_ht₂t:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueht₂:t₂.IsValue⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueht₂:t₂.IsValueh₁:t₁.IsValue→∃xσ₁t₂,t₁=<{λ~x:σ₁.t₂}>⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueht₂:t₂.IsValueh₁:t₁.IsValue→∃xσ₁t₂,t₁=<{λ~x:σ₁.t₂}>x:Stringσ:Tyv:Tmhv:t₁=<{λ~x:σ.v}>⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueht₂:t₂.IsValueh₁:t₁.IsValue→∃xσ₁t₂,t₁=<{λ~x:σ₁.t₂}>x:Stringσ:Tyv:Tmhv:t₁=<{λ~x:σ.v}>⊢ <{t₁t₂}>⟶substxt₂v;t:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueht₂:t₂.IsValueh₁:t₁.IsValue→∃xσ₁t₂,t₁=<{λ~x:σ₁.t₂}>x:Stringσ:Tyv:Tmhv:t₁=<{λ~x:σ.v}>⊢ <{(λ~x:σ.v)t₂}>⟶substxt₂vAll goals completed! 🐙-- t₂ is not a valuecase_ht₂t:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValueht₂:∃t',t₂⟶t'⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValuet₂':Tmht₂:t₂⟶t₂'⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:t₁.IsValuet₂':Tmht₂:t₂⟶t₂'⊢ <{t₁t₂}>⟶<{t₁t₂'}>;All goals completed! 🐙-- t₁ is not a valuecase_ht₁t:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ht₁:∃t',t₁⟶t'⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t't₁':Tmht₁:t₁⟶t₁'⊢ ∃t',<{t₁t₂}>⟶t't:Tmτ:TyΓ:Contextτ₁:Tyτ₂:Tyt₁:Tmt₂:Tmh₁:<{∅⊢~(t₁)⦂τ₂→τ₁}>h₂:<{∅⊢~(t₂)⦂~(τ₂)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t't₁':Tmht₁:t₁⟶t₁'⊢ <{t₁t₂}>⟶<{t₁'t₂}>;All goals completed! 🐙t:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'⊢ <{ift₁thent₂elset₃}>.IsValue∨∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'⊢ ∃t',<{ift₁thent₂elset₃}>⟶t';t:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'h✝:t₁.IsValue⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'h✝:∃t',t₁⟶t'⊢ ∃t',<{ift₁thent₂elset₃}>⟶t'-- t₁ is a valuecase_ht₁t:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ht₁:t₁.IsValue⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ht₁:t₁.IsValueh₁:t₁.IsValue→t₁=<{true}>∨t₁=<{false}>⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ht₁:t₁.IsValueh₁✝:t₁.IsValue→t₁=<{true}>∨t₁=<{false}>h₁:t₁=<{true}>⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ht₁:t₁.IsValueh₁✝:t₁.IsValue→t₁=<{true}>∨t₁=<{false}>h₁:t₁=<{false}>⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ht₁:t₁.IsValueh₁✝:t₁.IsValue→t₁=<{true}>∨t₁=<{false}>h₁:t₁=<{true}>⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ht₁:t₁.IsValueh₁✝:t₁.IsValue→t₁=<{true}>∨t₁=<{false}>h₁:t₁=<{false}>⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ih₁:∅=∅→<{false}>.IsValue∨∃t',<{false}>⟶t'ht₁:<{false}>.IsValueh₁:<{false}>.IsValue→<{false}>=<{true}>∨<{false}>=<{false}>⊢ ∃t',<{iffalsethent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ih₁:∅=∅→<{true}>.IsValue∨∃t',<{true}>⟶t'ht₁:<{true}>.IsValueh₁:<{true}>.IsValue→<{true}>=<{true}>∨<{true}>=<{false}>⊢ ∃t',<{iftruethent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ih₁:∅=∅→<{true}>.IsValue∨∃t',<{true}>⟶t'ht₁:<{true}>.IsValueh₁:<{true}>.IsValue→<{true}>=<{true}>∨<{true}>=<{false}>⊢ <{iftruethent₂elset₃}>⟶t₂;All goals completed! 🐙t:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ih₁:∅=∅→<{false}>.IsValue∨∃t',<{false}>⟶t'ht₁:<{false}>.IsValueh₁:<{false}>.IsValue→<{false}>=<{true}>∨<{false}>=<{false}>⊢ ∃t',<{iffalsethent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ih₁:∅=∅→<{false}>.IsValue∨∃t',<{false}>⟶t'ht₁:<{false}>.IsValueh₁:<{false}>.IsValue→<{false}>=<{true}>∨<{false}>=<{false}>⊢ <{iffalsethent₂elset₃}>⟶t₃;All goals completed! 🐙-- t₁ is not a valuecase_ht₁t:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t'ht₁:∃t',t₁⟶t'⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t't₁':Tmht₁:t₁⟶t₁'⊢ ∃t',<{ift₁thent₂elset₃}>⟶t't:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyh₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∅=∅→t₁.IsValue∨∃t',t₁⟶t'ih₂:∅=∅→t₂.IsValue∨∃t',t₂⟶t'ih₃:∅=∅→t₃.IsValue∨∃t',t₃⟶t't₁':Tmht₁:t₁⟶t₁'⊢ <{ift₁thent₂elset₃}>⟶<{ift₁'thent₂elset₃}>;All goals completed! 🐙t:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs:τ₁<:τ₂ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∅=∅→t₁.IsValue∨∃t',t₁⟶t'⊢ t₁.IsValue∨∃t',t₁⟶t't:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs:τ₁<:τ₂ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∅=∅→t₁.IsValue∨∃t',t₁⟶t'⊢ ∅=∅;All goals completed! 🐙-- Fill in products here later-- FILL IN HERE
The proof of the preservation theorem also becomes a little more
complex with the addition of subtyping. The reason is that, as
with the "inversion lemmas for subtyping" above, there are a
number of facts about the typing relation that are immediate from
the definition in the pure STLC (formally: that can be obtained
directly from the inversion tactic) but that require real proofs
in the presence of subtyping because there are multiple ways to
derive the same HasType statement.
The following inversion lemma tells us that, if we have a
derivation of some typing statement Γ ⊢ λ x : σ₁ . t₂ ⦂ τ whose
subject is an abstraction, then there must be some subderivation
giving a type to the body t₂.
Lemma: If Γ ⊢ λ x : σ₁ . t₂ ⦂ τ, then there is a type σ₂
such that x ↦ σ₁ ; Γ ⊢ t₂ ⦂ σ and σ₁ → σ₂ <: τ.
Notice that the lemma does not say, "then τ itself is an arrow
type" -- this is tempting, but false! (Why?)
Proof: Let Γ, x, σ₁, t₂ and τ be given as
described. Proceed by induction on the derivation of Γ ⊢ λ x : σ₁ . t₂ ⦂ τ.
The cases for var and app are vacuous
as those rules cannot be used to give a type to a syntactic
abstraction.
If the last step of the derivation is a use of abs then
there is a type τ₁₂ such that τ = σ₁ → τ₁₂ and x ↦ σ₁; Γ ⊢ t₂ ⦂ τ₁₂.
Picking τ₁₂ for σ₂ gives us what we
need, since σ₁ → τ₁₂ <: σ₁ → τ₁₂ follows from rfl.
If the last step of the derivation is a use of sub then
there is a type σ such that σ <: τ and Γ ⊢ λx : σ₁, t₂ ⦂ σ.
The IH for the typing subderivation tells us that there
is some type σ₂ with σ₁ → σ₂ <: σ and x↦σ₁; Γ ⊢ t₂ ⦂ σ₂.
Picking type σ₂ gives us what we need, since σ₁ → σ₂ <: τ then follows by trans.
-- Add your lemmas for products here when you get to that exercise
-- FILL IN HERE-- FILL IN HEREunexpected end of input
The inversion lemmas for typing and for subtyping between arrow
types can be packaged up as a useful "combination lemma" telling
us exactly what we'll actually require below.
When subtyping is involved proofs are generally easier
when done by induction on typing derivations, rather than on terms.
The substitution lemma is proved as for pure STLC, but using
induction on the typing derivation this time (see Exercise
substitution_preserves_typing_from_typing_ind in StlcProp).
The proof of preservation now proceeds pretty much as in earlier
chapters, using the substitution lemma at the appropriate point
and the inversion lemma from above to extract structural
information from typing assumptions.
Theorem (Preservation): If t, t' are terms and τ is a type
such that ∅ ⊢ t ⦂ τ and t ⟶ t', then ∅ ⊢ t' ⦂ τ.
Proof: Let t and τ be given such that ∅ ⊢ t ⦂ τ.
We proceed by induction on the structure of this typing
derivation. The abs, unit, tru, and fls cases
are vacuous because abstractions and constants don't step. Case
var is vacuous as well, since the context is empty.
If the final step of the derivation is by app, then there
are terms t₁ and t₂ and types τ₁ and τ₂ such that t = t₁ t₂,
τ = τ₂, ∅ ⊢ t₁ ⦂ τ₁ → τ₂, and ∅ ⊢ t₂ ⦂ τ₁.
By the definition of the step relation, there are three ways
t₁ t₂ can step. Cases app₁' and app₂ follow
immediately by the induction hypotheses for the typing
subderivations and a use of app.
Suppose instead t₁ t₂ steps by appAbs. Then t₁ = λ x:σ . τ₁₂
for some type σ and term τ₁₂, and t' = [x:=t₂] τ₁₂.
By lemma abs_arrow, we have τ₁ <: σ and x:σ₁ ⊢ t₂ ⦂ τ₂. It then follows by the substitution lemma (substitution_preserves_typing) that
∅ ⊢ [x:=t₂] τ₁₂ ⦂ τ₂ as desired.
If the final step of the derivation uses rule if, then
there are terms t₁, t₂, and t₃ such that t = if t₁ then t₂ else t₃,
with ∅ ⊢ t₁ ⦂ Bool and with ∅ ⊢ t₂ ⦂ τ and ∅ ⊢ t₃ ⦂ τ. Moreover, by the induction
hypothesis, if t₁ steps to t₁' then ∅ ⊢ t₁' : Bool.
There are three cases to consider, depending on which rule was
used to show t ⟶ t'.
If t ⟶ t' by rule if, then t' = if t₁' then t₂ else t₃ with
t₁ ⟶ t₁'. By the induction hypothesis,
∅ ⊢ t₁' ⦂ Bool, and so ∅ ⊢ t' ⦂ τ by
if.
If t ⟶ t' by rule ifTrue or ifFalse, then
either t' = t₂ or t' = t₃, and ∅ ⊢ t' ⦂ τ
follows by assumption.
If the final step of the derivation is by sub, then there
is a type σ such that σ <: τ and ∅ ⊢ t ⦂ σ. The
result is immediate by the induction hypothesis for the typing
subderivation and an application of sub.
Qed.
theorempreservation{tt':Tm}{τ:Ty}(ht:<{∅⊢~t⦂~τ}>)(hs:t⟶t'):<{∅⊢~t'⦂~τ}>:=byt:Tmt':Tmτ:Tyht:<{∅⊢~(t)⦂~(τ)}>hs:t⟶t'⊢ <{∅⊢~(t')⦂~(τ)}>generalizeheq:(∅:Context)=Γathtt:Tmt':Tmτ:Tyhs:t⟶t'Γ:Contextheq:∅=Γht:<{~(Γ)⊢~(t)⦂~(τ)}>⊢ <{~(Γ)⊢~(t')⦂~(τ)}>Alternative `snd` has not been providedAlternative `pair` has not been providedAlternative `fst` has not been providedinductionhtgeneralizingt'with(subst_varssndt:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyt':Tmhs:<{sndt✝}>⟶t'h✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∀{t':Tm},t✝⟶t'→∅=∅→<{∅⊢~(t')⦂τ₁✝×τ₂✝}>⊢ <{∅⊢~(t')⦂~(τ₂✝)}>;first-- discharge the goals where `t` doesn't step|inversionhssnd₁t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∀{t':Tm},t✝⟶t'→∅=∅→<{∅⊢~(t')⦂τ₁✝×τ₂✝}>t'✝:Tma✝:t✝⟶t'✝⊢ <{∅⊢sndt'✝⦂~(τ₂✝)}>sndPairt:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ <{∅⊢~(t')⦂~(τ₂✝)}><;>snd₁t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∀{t':Tm},t✝⟶t'→∅=∅→<{∅⊢~(t')⦂τ₁✝×τ₂✝}>t'✝:Tma✝:t✝⟶t'✝⊢ <{∅⊢sndt'✝⦂~(τ₂✝)}>sndPairt:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ <{∅⊢~(t')⦂~(τ₂✝)}>constructorsndPair.htt:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ <{∅⊢~(t')⦂~(?sndPair.τ₁)}>sndPair.hst:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ ?sndPair.τ₁<:τ₂✝sndPair.τ₁t:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ Ty<;>snd₁.htt:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∀{t':Tm},t✝⟶t'→∅=∅→<{∅⊢~(t')⦂τ₁✝×τ₂✝}>t'✝:Tma✝:t✝⟶t'✝⊢ <{∅⊢sndt'✝⦂~(?snd₁.τ₁)}>snd₁.hst:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∀{t':Tm},t✝⟶t'→∅=∅→<{∅⊢~(t')⦂τ₁✝×τ₂✝}>t'✝:Tma✝:t✝⟶t'✝⊢ ?snd₁.τ₁<:τ₂✝snd₁.τ₁t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∀{t':Tm},t✝⟶t'→∅=∅→<{∅⊢~(t')⦂τ₁✝×τ₂✝}>t'✝:Tma✝:t✝⟶t'✝⊢ TysndPair.htt:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ <{∅⊢~(t')⦂~(?sndPair.τ₁)}>sndPair.hst:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ ?sndPair.τ₁<:τ₂✝sndPair.τ₁t:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ Tysimp_allsndPair.τ₁t:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ Ty;doneAll goals completed! 🐙|try(inversionhssnd₁t:Tmτ:TyΓ:Contextt✝:Tmτ₁✝:Tyτ₂✝:Tyh✝:<{∅⊢~(t✝)⦂τ₁✝×τ₂✝}>h_ih✝:∀{t':Tm},t✝⟶t'→∅=∅→<{∅⊢~(t')⦂τ₁✝×τ₂✝}>t'✝:Tma✝:t✝⟶t'✝⊢ <{∅⊢sndt'✝⦂~(τ₂✝)}>sndPairt:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ <{∅⊢~(t')⦂~(τ₂✝)}>;apply_rulesusingStlcSubTypingsndPairt:Tmτ:TyΓ:Contextτ₁✝:Tyτ₂✝:Tyt':Tmv₁✝:Tma✝¹:v₁✝.IsValuea✝:t'.IsValueh✝:<{∅⊢(v₁✝,t')⦂τ₁✝×τ₂✝}>h_ih✝:∀{t'_1:Tm},<{(v₁✝,t')}>⟶t'_1→∅=∅→<{∅⊢~(t'_1)⦂τ₁✝×τ₂✝}>⊢ <{∅⊢~(t')⦂~(τ₂✝)}>;doneAll goals completed! 🐙))|appΓτ₁'τ₂'t₁'t₂h₁h₂ih₁ih₂=>appt:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₁':Tmt₂:Tmt':Tmhs:<{t₁'t₂}>⟶t'h₁:<{∅⊢~(t₁')⦂τ₂'→τ₁'}>h₂:<{∅⊢~(t₂)⦂~(τ₂')}>ih₁:∀{t':Tm},t₁'⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>⊢ <{∅⊢~(t')⦂~(τ₁')}>inversionhswith(try(constructorapp₂.h₁t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₁':Tmt₂:Tmh₁:<{∅⊢~(t₁')⦂τ₂'→τ₁'}>h₂:<{∅⊢~(t₂)⦂~(τ₂')}>ih₁:∀{t':Tm},t₁'⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>t₂'✝:Tma✝¹:t₁'.IsValuea✝:t₂⟶t₂'✝⊢ <{∅⊢~(t₁')⦂~?app₂.τ₂→τ₁'}>app₂.h₂t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₁':Tmt₂:Tmh₁:<{∅⊢~(t₁')⦂τ₂'→τ₁'}>h₂:<{∅⊢~(t₂)⦂~(τ₂')}>ih₁:∀{t':Tm},t₁'⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>t₂'✝:Tma✝¹:t₁'.IsValuea✝:t₂⟶t₂'✝⊢ <{∅⊢~(t₂'✝)⦂~(?app₂.τ₂)}>app₂.τ₂t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₁':Tmt₂:Tmh₁:<{∅⊢~(t₁')⦂τ₂'→τ₁'}>h₂:<{∅⊢~(t₂)⦂~(τ₂')}>ih₁:∀{t':Tm},t₁'⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>t₂'✝:Tma✝¹:t₁'.IsValuea✝:t₂⟶t₂'✝⊢ Ty<;>app₂.h₁t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₁':Tmt₂:Tmh₁:<{∅⊢~(t₁')⦂τ₂'→τ₁'}>h₂:<{∅⊢~(t₂)⦂~(τ₂')}>ih₁:∀{t':Tm},t₁'⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>t₂'✝:Tma✝¹:t₁'.IsValuea✝:t₂⟶t₂'✝⊢ <{∅⊢~(t₁')⦂~?app₂.τ₂→τ₁'}>app₂.h₂t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₁':Tmt₂:Tmh₁:<{∅⊢~(t₁')⦂τ₂'→τ₁'}>h₂:<{∅⊢~(t₂)⦂~(τ₂')}>ih₁:∀{t':Tm},t₁'⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>t₂'✝:Tma✝¹:t₁'.IsValuea✝:t₂⟶t₂'✝⊢ <{∅⊢~(t₂'✝)⦂~(?app₂.τ₂)}>app₂.τ₂t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₁':Tmt₂:Tmh₁:<{∅⊢~(t₁')⦂τ₂'→τ₁'}>h₂:<{∅⊢~(t₂)⦂~(τ₂')}>ih₁:∀{t':Tm},t₁'⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>t₂'✝:Tma✝¹:t₁'.IsValuea✝:t₂⟶t₂'✝⊢ Tyapply_rulesAll goals completed! 🐙;done))|appAbs_τ₂t₁h=>obtain⟨h₁,h₂⟩:=abs_arrowh₁appAbst:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ <{∅⊢~(substx✝t₂t₁)⦂~(τ₁')}>applysubstitution_preserves_typing(τ₁:=τ₂)appAbs.htt:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ <{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>appAbs.hvt:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ <{∅⊢~(t₂)⦂~(τ₂)}>·appAbs.htt:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ <{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>assumptionAll goals completed! 🐙·appAbs.hvt:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ <{∅⊢~(t₂)⦂~(τ₂)}>applyHasType.subappAbs.hv.htt:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ <{∅⊢~(t₂)⦂~(?appAbs.hv.τ₁)}>appAbs.hv.hst:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ ?appAbs.hv.τ₁<:τ₂appAbs.hv.τ₁t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ Ty<;>appAbs.hv.htt:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ <{∅⊢~(t₂)⦂~(?appAbs.hv.τ₁)}>appAbs.hv.hst:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ ?appAbs.hv.τ₁<:τ₂appAbs.hv.τ₁t:Tmτ:TyΓ:Contextτ₁':Tyτ₂':Tyt₂:Tmh₂✝:<{∅⊢~(t₂)⦂~(τ₂')}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₂')}>x✝:Stringτ₂:Tyt₁:Tmh₁✝:<{∅⊢λ~x✝:τ₂.t₁⦂τ₂'→τ₁'}>ih₁:∀{t':Tm},<{λ~x✝:τ₂.t₁}>⟶t'→∅=∅→<{∅⊢~(t')⦂τ₂'→τ₁'}>h:t₂.IsValueh₁:τ₂'<:τ₂h₂:<{~(x✝→ₚτ₂)⊢~(t₁)⦂~(τ₁')}>⊢ Tyapply_rulesusingStlcSubTypingAll goals completed! 🐙|iteΓt₁t₂t₃τh₁h₂h₃ih₁ih₂ih₃=>itet:Tmτ✝:TyΓ:Contextt₁:Tmt₂:Tmt₃:Tmτ:Tyt':Tmhs:<{ift₁thent₂elset₃}>⟶t'h₁:<{∅⊢~(t₁)⦂Bool}>h₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₁:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂Bool}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>ih₃:∀{t':Tm},t₃⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>⊢ <{∅⊢~(t')⦂~(τ)}>inversionhswith(try(constructorifFalse.htt:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>ih₃:∀{t':Tm},t₃⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>h₁:<{∅⊢false⦂Bool}>ih₁:∀{t':Tm},<{false}>⟶t'→∅=∅→<{∅⊢~(t')⦂Bool}>⊢ <{∅⊢~(t₃)⦂~(?ifFalse.τ₁)}>ifFalse.hst:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>ih₃:∀{t':Tm},t₃⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>h₁:<{∅⊢false⦂Bool}>ih₁:∀{t':Tm},<{false}>⟶t'→∅=∅→<{∅⊢~(t')⦂Bool}>⊢ ?ifFalse.τ₁<:τifFalse.τ₁t:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>ih₃:∀{t':Tm},t₃⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>h₁:<{∅⊢false⦂Bool}>ih₁:∀{t':Tm},<{false}>⟶t'→∅=∅→<{∅⊢~(t')⦂Bool}>⊢ Ty<;>ifFalse.htt:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>ih₃:∀{t':Tm},t₃⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>h₁:<{∅⊢false⦂Bool}>ih₁:∀{t':Tm},<{false}>⟶t'→∅=∅→<{∅⊢~(t')⦂Bool}>⊢ <{∅⊢~(t₃)⦂~(?ifFalse.τ₁)}>ifFalse.hst:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>ih₃:∀{t':Tm},t₃⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>h₁:<{∅⊢false⦂Bool}>ih₁:∀{t':Tm},<{false}>⟶t'→∅=∅→<{∅⊢~(t')⦂Bool}>⊢ ?ifFalse.τ₁<:τifFalse.τ₁t:Tmτ✝:TyΓ:Contextt₂:Tmt₃:Tmτ:Tyh₂:<{∅⊢~(t₂)⦂~(τ)}>h₃:<{∅⊢~(t₃)⦂~(τ)}>ih₂:∀{t':Tm},t₂⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>ih₃:∀{t':Tm},t₃⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ)}>h₁:<{∅⊢false⦂Bool}>ih₁:∀{t':Tm},<{false}>⟶t'→∅=∅→<{∅⊢~(t')⦂Bool}>⊢ Tysolve_by_elimusingStlcSubEvalAll goals completed! 🐙))|subΓt₁τ₁τ₂hthsih=>subt:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs✝:τ₁<:τ₂t':Tmhs:t₁⟶t'ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₁)}>⊢ <{∅⊢~(t')⦂~(τ₂)}>applyHasType.subsub.htt:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs✝:τ₁<:τ₂t':Tmhs:t₁⟶t'ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₁)}>⊢ <{∅⊢~(t')⦂~(?sub.τ₁)}>sub.hst:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs✝:τ₁<:τ₂t':Tmhs:t₁⟶t'ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₁)}>⊢ ?sub.τ₁<:τ₂sub.τ₁t:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs✝:τ₁<:τ₂t':Tmhs:t₁⟶t'ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₁)}>⊢ Ty<;>sub.htt:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs✝:τ₁<:τ₂t':Tmhs:t₁⟶t'ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₁)}>⊢ <{∅⊢~(t')⦂~(?sub.τ₁)}>sub.hst:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs✝:τ₁<:τ₂t':Tmhs:t₁⟶t'ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₁)}>⊢ ?sub.τ₁<:τ₂sub.τ₁t:Tmτ:TyΓ:Contextt₁:Tmτ₁:Tyτ₂:Tyhs✝:τ₁<:τ₂t':Tmhs:t₁⟶t'ht:<{∅⊢~(t₁)⦂~(τ₁)}>ih:∀{t':Tm},t₁⟶t'→∅=∅→<{∅⊢~(t')⦂~(τ₁)}>⊢ Tysolve_by_elimusingStlcSubTypingAll goals completed! 🐙-- FILL IN HERE
This formalization of the STLC with subtyping omits record
types for brevity. If we want to deal with them more seriously,
we have two choices.
First, we can treat them as part of the core language, writing
down proper syntax, typing, and subtyping rules for them.
On the other hand, if we are treating them as a derived form that
is desugared in the parser, then we shouldn't need any new rules:
we should just check that the existing rules for subtyping product
and Unit types give rise to reasonable rules for record
subtyping via this encoding. To do this, we just need to make one
small change to the encoding described earlier: instead of using
Unit as the base case in the encoding of tuples and the "don't
care" placeholder in the encoding of records, we use ⊤. So:
The encoding of record values doesn't change at all. It is
easy (and instructive) to check that the subtyping rules above are
validated by the encoding.
Exercise★★(variations) (Manually graded)
Each part of this problem suggests a different way of changing the
definition of the STLC with Unit and subtyping. (These changes
are not cumulative: each part starts from the original language.)
In each part, list which properties (Progress, Preservation, both,
or neither) become false. If a property becomes false, give a
counterexample.
Adding pairs, projections, and product types to the system we have
defined is a relatively straightforward matter. Carry out this
extension by modifying the definitions and proofs above:
Constructors for pairs, first and second projections, and
product types have already been added to the definitions of
Ty and Tm. Also, the definition of substitution has been
extended.
Extend the surrounding definitions accordingly (refer to chapter MoreStlc):
Extend the proofs of progress, preservation, and all their
supporting lemmas to deal with the new constructs. (You'll also
need to add a couple of completely new lemmas.)