text through <:-refl
This commit is contained in:
parent
b6b128c337
commit
a8682af3d4
1 changed files with 290 additions and 64 deletions
|
@ -10,6 +10,68 @@ next : /Bisimulation/
|
|||
module plfa.part2.Subtyping where
|
||||
```
|
||||
|
||||
This chapter introduces *subtyping*, a concept that plays an important
|
||||
role in object-oriented languages. Subtyping enables code to be more
|
||||
reusable by allowing code work on objects of many different
|
||||
types. Thus, subtyping provides a kind of polymorphism. In general, a
|
||||
type `A` can be a subtype of another type `B`, written `A <: B`, when
|
||||
an object of type `A` has all the capabilities expected of something
|
||||
of type `B`. Or put another way, a type `A` can be a subtype of type
|
||||
`B` when it is safe to substitute something of type `A` into code that
|
||||
is expected something of type `B`. This is know as the *Liskov
|
||||
Substitution Principle*. Given this semantic meaning of subtyping, a
|
||||
subtype relation should always be reflexive and transitive. Given a
|
||||
subtype relation `A <: B`, we refer to `B` as the supertype of `A`.
|
||||
|
||||
To formulate a type system for a language with subtyping, one simply
|
||||
adds the *subsumption rule*, which states that a term of type `A`
|
||||
can also have type `B` if `A` is a subtype of `B`.
|
||||
|
||||
⊢<: : ∀{Γ M A B}
|
||||
→ Γ ⊢ M ⦂ A → A <: B
|
||||
--------------------
|
||||
→ Γ ⊢ M ⦂ B
|
||||
|
||||
In this chapter we study subtyping in the relatively simple context of
|
||||
records and record types. A *record* is a grouping of named values,
|
||||
called *fields*. For example, one could represent a point on the
|
||||
cartesian plane with the following record.
|
||||
|
||||
{ x = 4, y = 1 }
|
||||
|
||||
A *record type* gives a type for each field. In the following, we
|
||||
specify that the fields `x` and `y` both have type ``ℕ`.
|
||||
|
||||
{ x : `ℕ, y : `ℕ }
|
||||
|
||||
One record type is a subtype of another if it has all of the fields of
|
||||
the supertype and if the types of those fields are subtypes of the
|
||||
corresponding fields in the supertype. So, for example, a point in
|
||||
three dimensions is a subtype of a point in two dimensions.
|
||||
|
||||
{ x : `ℕ, y : `ℕ, z : `ℕ } <: { x : `ℕ, y : `ℕ }
|
||||
|
||||
The elimination for for records is field access (aka. projection),
|
||||
written `M # l`, and whose dynamic semantics is defined by the
|
||||
following reduction rule, which says that accessing the field `lⱼ`
|
||||
returns the value stored at that field.
|
||||
|
||||
{l₁=v₁, ..., lⱼ=vⱼ, ..., lᵢ=vᵢ} # lⱼ —→ vⱼ
|
||||
|
||||
In this chapter we add records and record types to the simply typed
|
||||
lambda calculus (STLC) and prove type safety. The choice between
|
||||
extrinsic and intrinsic typing is made more interesting by the
|
||||
presense of subtyping. If we wish to include the subsumption rule,
|
||||
then we cannot use intrinsicly typed terms, as intrinsic terms only
|
||||
allow for syntax-directed rules, and subsumption is not syntax
|
||||
directed. A standard alternative to the subsumption rule is to
|
||||
instead use subtyping in the typing rules for the elimination forms,
|
||||
an approach called algorithmic typing. Here we choose to include the
|
||||
subsumption rule and use extrinsic typing, but we give an exercise at
|
||||
the end for the reader to explore algorithmic typing with intrinsic
|
||||
terms.
|
||||
|
||||
|
||||
## Imports
|
||||
|
||||
```
|
||||
|
@ -27,16 +89,21 @@ open import Data.Vec.Membership.DecPropositional _≟_ using (_∈?_)
|
|||
open import Data.Vec.Membership.Propositional.Properties using (∈-lookup)
|
||||
open import Data.Vec.Relation.Unary.Any using (here; there)
|
||||
import Relation.Binary.PropositionalEquality as Eq
|
||||
open Eq using (_≡_; _≢_; refl; sym; trans; cong)
|
||||
open Eq using (_≡_; refl; sym; trans; cong)
|
||||
open import Relation.Nullary using (Dec; yes; no; ¬_)
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
The syntax includes that of the STLC with a few additions regarding
|
||||
records that we explain in the following sections.
|
||||
|
||||
```
|
||||
infix 4 _⊢_⦂_
|
||||
infix 4 _⊢*_⦂_
|
||||
infix 4 _∋_⦂_
|
||||
infixl 5 _,_
|
||||
infix 5 _[_]
|
||||
|
||||
infixr 7 _⇒_
|
||||
|
||||
|
@ -46,22 +113,47 @@ infixl 7 _·_
|
|||
infix 8 `suc_
|
||||
infix 9 `_
|
||||
infixl 7 _#_
|
||||
|
||||
infix 4 _⊆_
|
||||
infix 5 {_⦂_}
|
||||
infix 5 {_:=_}
|
||||
```
|
||||
|
||||
## Record Fields and their Properties
|
||||
|
||||
## Record Field Names, Functions and Properties
|
||||
|
||||
We represent field identifiers (aka. names) as strings.
|
||||
We represent field names as strings.
|
||||
|
||||
```
|
||||
Name : Set
|
||||
Name = String
|
||||
```
|
||||
|
||||
The field identifiers of a record will be stored in a sequence,
|
||||
specifically Agda's `Vec` type. We require that the field names of a
|
||||
record be distinct, which we define as follows.
|
||||
A record is traditionally written as follows
|
||||
|
||||
{ l₁ = M₁, ..., lᵢ = Mᵢ }
|
||||
|
||||
so a natural representation is a list of label-term pairs.
|
||||
However, we find it more convenient to represent records as a pair of
|
||||
vectors (Agda's `Vec` type), one vector of fields and one vector of terms:
|
||||
|
||||
l₁, ..., lᵢ
|
||||
M₁, ..., Mᵢ
|
||||
|
||||
This representation has the advantage that the subscript notation `lᵢ`
|
||||
corresponds to indexing into a vector.
|
||||
|
||||
Likewise, a record type, traditionally written as
|
||||
|
||||
{ l₁ : A₁, ..., lᵢ : Aᵢ }
|
||||
|
||||
will be represented as a pair of vectors, one vector of fields and one
|
||||
vector of types.
|
||||
|
||||
l₁, ..., lᵢ
|
||||
A₁, ..., Aᵢ
|
||||
|
||||
The field names of a record type must be distinct, which we define as
|
||||
follows.
|
||||
```
|
||||
distinct : ∀{A : Set}{n} → Vec A n → Set
|
||||
distinct [] = ⊤
|
||||
|
@ -69,25 +161,24 @@ distinct (x ∷ xs) = ¬ (x ∈ xs) × distinct xs
|
|||
```
|
||||
|
||||
For vectors of distinct elements, lookup is injective.
|
||||
|
||||
```
|
||||
distinct-lookup-inj : ∀ {n}{ls : Vec Name n}{i j : Fin n}
|
||||
→ distinct ls → lookup ls i ≡ lookup ls j
|
||||
→ i ≡ j
|
||||
distinct-lookup-inj {.(suc _)} {x ∷ ls} {zero} {zero} ⟨ x∉ls , dls ⟩ lsij = refl
|
||||
distinct-lookup-inj {.(suc _)} {x ∷ ls} {zero} {suc j} ⟨ x∉ls , dls ⟩ refl =
|
||||
distinct-lookup-inj {ls = x ∷ ls} {zero} {zero} ⟨ x∉ls , dls ⟩ lsij = refl
|
||||
distinct-lookup-inj {ls = x ∷ ls} {zero} {suc j} ⟨ x∉ls , dls ⟩ refl =
|
||||
⊥-elim (x∉ls (∈-lookup j ls))
|
||||
distinct-lookup-inj {.(suc _)} {x ∷ ls} {suc i} {zero} ⟨ x∉ls , dls ⟩ refl =
|
||||
distinct-lookup-inj {ls = x ∷ ls} {suc i} {zero} ⟨ x∉ls , dls ⟩ refl =
|
||||
⊥-elim (x∉ls (∈-lookup i ls))
|
||||
distinct-lookup-inj {.(suc _)} {x ∷ ls} {suc i} {suc j} ⟨ x∉ls , dls ⟩ lsij =
|
||||
distinct-lookup-inj {ls = x ∷ ls} {suc i} {suc j} ⟨ x∉ls , dls ⟩ lsij =
|
||||
cong suc (distinct-lookup-inj dls lsij)
|
||||
```
|
||||
|
||||
We shall need to convert from an irrelevent proof of distinctness to a
|
||||
relevant one. In general, this can be accomplished for any decidable
|
||||
predicate. The following is a decision procedure for whether a vector
|
||||
relevant one. In general, the laundering of irrelevant proofs into
|
||||
relevant ones is easy to do when the predicate in question is
|
||||
decidable. The following is a decision procedure for whether a vector
|
||||
is distinct.
|
||||
|
||||
```
|
||||
distinct? : ∀{n} → (xs : Vec Name n) → Dec (distinct xs)
|
||||
distinct? [] = yes tt
|
||||
|
@ -100,9 +191,9 @@ distinct? (x ∷ xs)
|
|||
... | no ¬dxs = no λ x₁ → ¬dxs (proj₂ x₁)
|
||||
```
|
||||
|
||||
With the decision procedure in hand, we can launder an irrelevant
|
||||
proof into a relevant one.
|
||||
|
||||
With this decision procedure in hand, the define the following
|
||||
function for laundering irrelevant proofs of distinctness into
|
||||
relevant ones.
|
||||
```
|
||||
distinct-relevant : ∀ {n}{fs : Vec Name n} .(d : distinct fs) → distinct fs
|
||||
distinct-relevant {n}{fs} d
|
||||
|
@ -111,35 +202,25 @@ distinct-relevant {n}{fs} d
|
|||
... | no ¬dfs = ⊥-elimi (¬dfs d)
|
||||
```
|
||||
|
||||
The fields of one record are a subset of the fields of another record
|
||||
if every field of the first is also a field of the second.
|
||||
|
||||
The fields of one record are a *subset* of the fields of another
|
||||
record if every field of the first is also a field of the second.
|
||||
```
|
||||
_⊆_ : ∀{n m} → Vec Name n → Vec Name m → Set
|
||||
xs ⊆ ys = (x : Name) → x ∈ xs → x ∈ ys
|
||||
```
|
||||
|
||||
This subset relation is reflexive.
|
||||
|
||||
This subset relation is reflexive and transitive.
|
||||
```
|
||||
⊆-refl : ∀{n}{ls : Vec Name n} → ls ⊆ ls
|
||||
⊆-refl {n}{ls} = λ x x∈ls → x∈ls
|
||||
```
|
||||
|
||||
It is also transitive.
|
||||
|
||||
```
|
||||
⊆-trans : ∀{l n m}{ns : Vec Name n}{ms : Vec Name m}{ls : Vec Name l}
|
||||
→ ns ⊆ ms → ms ⊆ ls
|
||||
→ ns ⊆ ls
|
||||
→ ns ⊆ ms → ms ⊆ ls → ns ⊆ ls
|
||||
⊆-trans {l}{n}{m}{ns}{ms}{ls} ns⊆ms ms⊆ls = λ x z → ms⊆ls x (ns⊆ms x z)
|
||||
```
|
||||
|
||||
|
||||
|
||||
If `y` is an element of vector `xs`, then `y` is at some index `i` of
|
||||
the vector.
|
||||
|
||||
```
|
||||
lookup-∈ : ∀{ℓ}{A : Set ℓ}{n} {xs : Vec A n}{y}
|
||||
→ y ∈ xs
|
||||
|
@ -152,7 +233,6 @@ lookup-∈ {xs = x ∷ xs} (there y∈xs)
|
|||
|
||||
If one vector `ns` is a subset of another `ms`, then for any element
|
||||
`lookup ns i`, there is an equal element in `ms` at some index.
|
||||
|
||||
```
|
||||
lookup-⊆ : ∀{n m : ℕ}{ns : Vec Name n}{ms : Vec Name m}{i : Fin n}
|
||||
→ ns ⊆ ms
|
||||
|
@ -168,17 +248,19 @@ lookup-⊆ {suc n} {m} {x ∷ ns} {ms} {suc i} x∷ns⊆ms =
|
|||
## Types
|
||||
|
||||
```
|
||||
infix 5 {_⦂_}
|
||||
|
||||
data Type : Set where
|
||||
_⇒_ : Type → Type → Type
|
||||
`ℕ : Type
|
||||
{_⦂_} : {n : ℕ} (ls : Vec Name n) (As : Vec Type n) → .{d : distinct ls} → Type
|
||||
```
|
||||
|
||||
We do not want the details of the proof of distinctness to affect
|
||||
whether two record types are equal, so we declare that parameter to be
|
||||
irrelevant by placing a `.` in front of it.
|
||||
In addition to function types `A ⇒ B` and natural numbers ``ℕ`, we
|
||||
have the record type `{ ls ⦂ As }`, where `ls` is a vector of field
|
||||
names and `As` is a vector of types, as discussed above. We require
|
||||
that the field names be distinct, but we do not want the details of
|
||||
the proof of distinctness to affect whether two record types are
|
||||
equal, so we declare that parameter to be irrelevant by placing a `.`
|
||||
in front of it.
|
||||
|
||||
|
||||
## Subtyping
|
||||
|
@ -203,11 +285,26 @@ data _<:_ : Type → Type → Set where
|
|||
→ { ks ⦂ Ss } {d1} <: { ls ⦂ Ts } {d2}
|
||||
```
|
||||
|
||||
The first premise of the record subtyping rule (`<:-rcd`) expresses
|
||||
_width subtyping_ by requiring that all the labels in `ls` are also in
|
||||
`ks`. So it allows the hiding of fields when going from a subtype to a
|
||||
supertype.
|
||||
The rule `<:-nat` simply states that ``ℕ` is a subtype of itself.
|
||||
|
||||
The rule `<:-fun` is the traditional rule for function types, which is
|
||||
best understood with the following diagram. It answers the question,
|
||||
when can a function of type `A ⇒ B` be used in place of a function of
|
||||
type `C ⇒ D`. It shall be called with an argument of type `C`, so
|
||||
we'll need to convert from `C` to `A`. We can then call the function
|
||||
to get the result of type `B`. Finally, we need to convert from `B` to
|
||||
`D`.
|
||||
|
||||
C <: A
|
||||
⇓ ⇓
|
||||
D :> B
|
||||
|
||||
|
||||
The record subtyping rule (`<:-rcd`) characterizes when a record of
|
||||
one type can safely be used in place of another record type. The
|
||||
first premise of the rule expresses _width subtyping_ by requiring
|
||||
that all the field names in `ls` are also in `ks`. So it allows the
|
||||
hiding of fields when going from a subtype to a supertype.
|
||||
The second premise of the record subtyping rule (`<:-rcd`) expresses
|
||||
_depth subtyping_, that is, it allows the types of the fields to
|
||||
change according to subtyping. The following is an abbreviation for
|
||||
|
@ -221,11 +318,11 @@ _⦂_<:_⦂_ {m}{n} ks Ss ls Ts = (∀{i : Fin n}{j : Fin m}
|
|||
|
||||
## Subtyping is Reflexive
|
||||
|
||||
The proof that subtyping is reflexive does not go by induction on the
|
||||
type because of the `<:-rcd` rule. We instead use induction on the
|
||||
size of the type. So we first define size of a type, and the size of a
|
||||
In this section we prove that subtyping is reflexive, that is, `A <:
|
||||
A` for any type `A`. The proof does not go by induction on the type
|
||||
`A` because of the `<:-rcd` rule. We instead use induction on the size
|
||||
of the type. So we first define size of a type, and the size of a
|
||||
vector of types, as follows.
|
||||
|
||||
```
|
||||
ty-size : (A : Type) → ℕ
|
||||
vec-ty-size : ∀ {n : ℕ} → (As : Vec Type n) → ℕ
|
||||
|
@ -239,7 +336,6 @@ vec-ty-size {n} (x ∷ xs) = ty-size x + vec-ty-size xs
|
|||
```
|
||||
|
||||
The size of a type is always positive.
|
||||
|
||||
```
|
||||
ty-size-pos : ∀ {A} → 0 < ty-size A
|
||||
ty-size-pos {A ⇒ B} = s≤s z≤n
|
||||
|
@ -249,7 +345,6 @@ ty-size-pos {{ fs ⦂ As } } = s≤s z≤n
|
|||
|
||||
If a vector of types has a size smaller than `n`, then so is any type
|
||||
in the vector.
|
||||
|
||||
```
|
||||
lookup-vec-ty-size : ∀{n}{k} {As : Vec Type k} {j}
|
||||
→ vec-ty-size As ≤ n
|
||||
|
@ -260,7 +355,6 @@ lookup-vec-ty-size {n} {suc k} {A ∷ As} {suc j} As≤n =
|
|||
```
|
||||
|
||||
Here is the proof of reflexivity, by induction on the size of the type.
|
||||
|
||||
```
|
||||
<:-refl-aux : ∀{n}{A}{m : ty-size A ≤ n} → A <: A
|
||||
<:-refl-aux {0}{A}{m}
|
||||
|
@ -270,17 +364,37 @@ Here is the proof of reflexivity, by induction on the size of the type.
|
|||
... | ()
|
||||
<:-refl-aux {suc n}{`ℕ}{m} = <:-nat
|
||||
<:-refl-aux {suc n}{A ⇒ B}{m} =
|
||||
let a = ≤-pred m in
|
||||
<:-fun (<:-refl-aux{n}{A}{m+n≤o⇒m≤o (ty-size A) a })
|
||||
(<:-refl-aux{n}{B}{m+n≤o⇒n≤o (ty-size A) a})
|
||||
<:-refl-aux {suc n}{{ fs ⦂ As } {d} }{m} = <:-rcd {d1 = d}{d2 = d} ⊆-refl G
|
||||
let A<:A = <:-refl-aux {n}{A}{m+n≤o⇒m≤o _ (≤-pred m) } in
|
||||
let B<:B = <:-refl-aux {n}{B}{m+n≤o⇒n≤o _ (≤-pred m) } in
|
||||
<:-fun A<:A B<:B
|
||||
<:-refl-aux {suc n}{{ ls ⦂ As } {d} }{m} = <:-rcd {d1 = d}{d2 = d} ⊆-refl G
|
||||
where
|
||||
G : fs ⦂ As <: fs ⦂ As
|
||||
G : ls ⦂ As <: ls ⦂ As
|
||||
G {i}{j} lij rewrite distinct-lookup-inj (distinct-relevant d) lij =
|
||||
let As[i]≤n = lookup-vec-ty-size {As = As}{i} (≤-pred m) in
|
||||
<:-refl-aux {n}{lookup As i}{As[i]≤n}
|
||||
```
|
||||
The theorem statement uses `n` as an upper bound on the size of the type `A`
|
||||
and proceeds by induction on `n`.
|
||||
|
||||
* If it is `0`, then we have a contradiction because the size of a type is always positive.
|
||||
|
||||
* If it is `suc n`, we proceed by cases on the type `A`.
|
||||
|
||||
* If it is ``ℕ`, then we have ``ℕ <: `ℕ` by rule `<:-nat`.
|
||||
* If it is `A ⇒ B`, then by induction we have `A <: A` and `B <: B`.
|
||||
We conclude that `A ⇒ B <: A ⇒ B` by rule `<:-fun`.
|
||||
* If it is `{ ls ⦂ As }`, then it suffices to prove that
|
||||
`ls ⊆ ls` and `ls ⦂ As <: ls ⦂ As`. The former is proved by `⊆-refl`.
|
||||
Regarding the latter, we need to show that for any `i` and `j`,
|
||||
`lookup ls j ≡ lookup ls i` implies `lookup As j <: lookup As i`.
|
||||
Because `lookup` is injective on distinct vectors, we have `i ≡ j`.
|
||||
We then use the induction hypothesis to show that
|
||||
`lookup As i <: lookup As i`, noting that the size of
|
||||
`lookup As i` is less-than-or-equal to the size of `As`, which
|
||||
is one smaller than the size of `{ ls ⦂ As }`.
|
||||
|
||||
The following corollary packages up reflexivity for ease of use.
|
||||
```
|
||||
<:-refl : ∀{A} → A <: A
|
||||
<:-refl {A} = <:-refl-aux {ty-size A}{A}{≤-refl}
|
||||
|
@ -302,10 +416,10 @@ already proved the two lemmas needed in the case for `<:-rcd`:
|
|||
<:-trans {.`ℕ} {`ℕ} {.`ℕ} <:-nat <:-nat = <:-nat
|
||||
<:-trans {{ ls ⦂ As }{d1} } {{ ms ⦂ Bs } {d2} } {{ ns ⦂ Cs } {d3} }
|
||||
(<:-rcd ms⊆ls As<:Bs) (<:-rcd ns⊆ms Bs<:Cs) =
|
||||
<:-rcd (⊆-trans ns⊆ms ms⊆ls) G
|
||||
<:-rcd (⊆-trans ns⊆ms ms⊆ls) As<:Cs
|
||||
where
|
||||
G : ls ⦂ As <: ns ⦂ Cs
|
||||
G {i}{j} lij
|
||||
As<:Cs : ls ⦂ As <: ns ⦂ Cs
|
||||
As<:Cs {i}{j} lij
|
||||
with lookup-⊆ {i = i} ns⊆ms
|
||||
... | ⟨ k , lik ⟩
|
||||
with lookup-⊆ {i = k} ms⊆ls
|
||||
|
@ -338,14 +452,14 @@ data _∋_⦂_ : Context → ℕ → Type → Set where
|
|||
→ Γ , B ∋ (suc x) ⦂ A
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Terms and the typing judgment
|
||||
|
||||
```
|
||||
Id : Set
|
||||
Id = ℕ
|
||||
```
|
||||
|
||||
```
|
||||
data Term : Set where
|
||||
`_ : Id → Term
|
||||
ƛ_ : Term → Term
|
||||
|
@ -686,13 +800,13 @@ progress (⊢case ⊢L ⊢M ⊢N) with progress ⊢L
|
|||
... | C-zero = step β-zero
|
||||
... | C-suc CL = step (β-suc (value CL))
|
||||
progress (⊢μ ⊢M) = step β-μ
|
||||
progress (⊢# {Γ}{A}{M}{n}{fs}{As}{d}{i}{f} ⊢R lif liA)
|
||||
progress (⊢# {Γ}{A}{M}{n}{ls}{As}{d}{i}{f} ⊢R lif liA)
|
||||
with progress ⊢R
|
||||
... | step R—→R′ = step (ξ-# R—→R′)
|
||||
... | done VR
|
||||
with canonical ⊢R VR
|
||||
... | C-rcd ⊢Ms dks (<:-rcd fs⊆fs' _)
|
||||
with lookup-⊆ {i = i} fs⊆fs'
|
||||
... | C-rcd ⊢Ms dks (<:-rcd ls⊆ls' _)
|
||||
with lookup-⊆ {i = i} ls⊆ls'
|
||||
... | ⟨ k , eq ⟩ rewrite eq = step (β-# {j = k} lif)
|
||||
progress (⊢rcd x d) = done V-rcd
|
||||
progress (⊢<: {A = A}{B} ⊢M A<:B) = progress ⊢M
|
||||
|
@ -732,7 +846,7 @@ rename-pres ρ⦂ (⊢` ∋w) = ⊢` (ρ⦂ ∋w)
|
|||
rename-pres {ρ = ρ} ρ⦂ (⊢ƛ ⊢N) = ⊢ƛ (rename-pres {ρ = ext ρ} (ext-pres {ρ = ρ} ρ⦂) ⊢N)
|
||||
rename-pres {ρ = ρ} ρ⦂ (⊢· ⊢L ⊢M)= ⊢· (rename-pres {ρ = ρ} ρ⦂ ⊢L) (rename-pres {ρ = ρ} ρ⦂ ⊢M)
|
||||
rename-pres {ρ = ρ} ρ⦂ (⊢μ ⊢M) = ⊢μ (rename-pres {ρ = ext ρ} (ext-pres {ρ = ρ} ρ⦂) ⊢M)
|
||||
rename-pres ρ⦂ (⊢rcd ⊢Ms dfs) = ⊢rcd (ren-vec-pres ρ⦂ ⊢Ms ) dfs
|
||||
rename-pres ρ⦂ (⊢rcd ⊢Ms dls) = ⊢rcd (ren-vec-pres ρ⦂ ⊢Ms ) dls
|
||||
rename-pres {ρ = ρ} ρ⦂ (⊢# {d = d} ⊢R lif liA) = ⊢# {d = d}(rename-pres {ρ = ρ} ρ⦂ ⊢R) lif liA
|
||||
rename-pres {ρ = ρ} ρ⦂ (⊢<: ⊢M lt) = ⊢<: (rename-pres {ρ = ρ} ρ⦂ ⊢M) lt
|
||||
rename-pres ρ⦂ ⊢zero = ⊢zero
|
||||
|
@ -777,7 +891,7 @@ subst-pres σ⦂ (⊢` eq) = σ⦂ eq
|
|||
subst-pres {σ = σ} σ⦂ (⊢ƛ ⊢N) = ⊢ƛ (subst-pres{σ = exts σ}(exts-pres {σ = σ} σ⦂) ⊢N)
|
||||
subst-pres {σ = σ} σ⦂ (⊢· ⊢L ⊢M) = ⊢· (subst-pres{σ = σ} σ⦂ ⊢L) (subst-pres{σ = σ} σ⦂ ⊢M)
|
||||
subst-pres {σ = σ} σ⦂ (⊢μ ⊢M) = ⊢μ (subst-pres{σ = exts σ} (exts-pres{σ = σ} σ⦂) ⊢M)
|
||||
subst-pres σ⦂ (⊢rcd ⊢Ms dfs) = ⊢rcd (subst-vec-pres σ⦂ ⊢Ms ) dfs
|
||||
subst-pres σ⦂ (⊢rcd ⊢Ms dls) = ⊢rcd (subst-vec-pres σ⦂ ⊢Ms ) dls
|
||||
subst-pres {σ = σ} σ⦂ (⊢# {d = d} ⊢R lif liA) =
|
||||
⊢# {d = d} (subst-pres {σ = σ} σ⦂ ⊢R) lif liA
|
||||
subst-pres {σ = σ} σ⦂ (⊢<: ⊢N lt) = ⊢<: (subst-pres {σ = σ} σ⦂ ⊢N) lt
|
||||
|
@ -848,3 +962,115 @@ preserve (⊢# {ls = ls′}{i = i} ⊢M refl refl) (β-# {ls = ks}{Ms}{j = j} ks
|
|||
⊢<: ⊢Ms[k] (As<:Bs (sym ls′[i]=ks[k]))
|
||||
preserve (⊢<: ⊢M A<:B) M—→N = ⊢<: (preserve ⊢M M—→N) A<:B
|
||||
```
|
||||
|
||||
#### Exercise `intrinsic-records` (stretch)
|
||||
|
||||
Formulate the language of this chapter, STLC with records, using
|
||||
intrinsicaly typed terms. This requires taking an algorithmic approach
|
||||
to the type system, which means that there is no subsumption rule and
|
||||
instead subtyping is used in the elimination rules. For example,
|
||||
the rule for function application would be:
|
||||
|
||||
_·_ : ∀ {Γ A B C}
|
||||
→ Γ ⊢ A ⇒ B
|
||||
→ Γ ⊢ C
|
||||
→ C <: A
|
||||
-------
|
||||
→ Γ ⊢ B
|
||||
|
||||
#### Exercise `type-check-records` (practice)
|
||||
|
||||
Write a recursive function whose input is a `Term` and whose output
|
||||
is a typing derivation for that term, if one exists.
|
||||
|
||||
type-check : (M : Term) → (Γ : Context) → Maybe (Σ[ A ∈ Type ] Γ ⊢ M ⦂ A)
|
||||
|
||||
### Exercise `variants` (recommended)
|
||||
|
||||
Add variants to the language of this Chapter and update the proofs of
|
||||
progress and preservation. The variant type is a generalization of a
|
||||
sum type, similar to the way the record type is a generalization of
|
||||
product. The following summarizes the treatement of variants in the
|
||||
book Types and Programming Languages. A variant type is traditionally
|
||||
written:
|
||||
|
||||
〈l₁:A₁, ..., lᵤ:Aᵤ〉
|
||||
|
||||
The term for introducing a variant is
|
||||
|
||||
〈l=t〉
|
||||
|
||||
and the term for eliminating a variant is
|
||||
|
||||
case L of 〈l₁=x₁〉 ⇒ M₁ | ... | 〈lᵤ=xᵤ〉 ⇒ Mᵤ
|
||||
|
||||
The typing rules for these terms are
|
||||
|
||||
(T-Variant)
|
||||
Γ ⊢ Mⱼ : Aⱼ
|
||||
---------------------------------
|
||||
Γ ⊢ 〈lⱼ=Mⱼ〉 : 〈l₁=A₁, ... , lᵤ=Aᵤ〉
|
||||
|
||||
|
||||
(T-Case)
|
||||
Γ ⊢ L : 〈l₁=A₁, ... , lᵤ=Aᵤ〉
|
||||
∀ i ∈ 1..u, Γ,xᵢ:Aᵢ ⊢ Mᵢ : B
|
||||
---------------------------------------------------
|
||||
Γ ⊢ case L of 〈l₁=x₁〉 ⇒ M₁ | ... | 〈lᵤ=xᵤ〉 ⇒ Mᵤ : B
|
||||
|
||||
The non-algorithmic subtyping rules for variants are
|
||||
|
||||
(S-VariantWidth)
|
||||
------------------------------------------------------------
|
||||
〈l₁=A₁, ..., lᵤ=Aᵤ〉 <: 〈l₁=A₁, ..., lᵤ=Aᵤ, ..., lᵤ₊ₓ=Aᵤ₊ₓ〉
|
||||
|
||||
(S-VariantDepth)
|
||||
∀ i ∈ 1..u, Aᵢ <: Bᵢ
|
||||
---------------------------------------------
|
||||
〈l₁=A₁, ..., lᵤ=Aᵤ〉 <: 〈l₁=B₁, ..., lᵤ=Bᵤ〉
|
||||
|
||||
(S-VariantPerm)
|
||||
∀i∈1..u, ∃j∈1..u, kⱼ = lᵢ and Aⱼ = Bᵢ
|
||||
----------------------------------------------
|
||||
〈k₁=A₁, ..., kᵤ=Aᵤ〉 <: 〈l₁=B₁, ..., lᵤ=Bᵤ〉
|
||||
|
||||
Come up with an algorithmic subtyping rule for variant types.
|
||||
|
||||
### Exercise `<:-alternative` (stretch)
|
||||
|
||||
Revise this formalization of records with subtyping (including proofs
|
||||
of progress and preservation) to use the non-algorithmic subtyping
|
||||
rules for Chapter 15 of TAPL, which we list here:
|
||||
|
||||
(S-RcdWidth)
|
||||
--------------------------------------------------------------
|
||||
{ l₁:A₁, ..., lᵤ:Aᵤ, ..., lᵤ₊ₓ:Aᵤ₊ₓ } <: { l₁:A₁, ..., lᵤ:Aᵤ }
|
||||
|
||||
(S-RcdDepth)
|
||||
∀i∈1..u, Aᵢ <: Bᵢ
|
||||
----------------------------------------------
|
||||
{ l₁:A₁, ..., lᵤ:Aᵤ } <: { l₁:B₁, ..., lᵤ:Bᵤ }
|
||||
|
||||
(S-RcdPerm)
|
||||
∀i∈1..u, ∃j∈1..u, kⱼ = lᵢ and Aⱼ = Bᵢ
|
||||
----------------------------------------------
|
||||
{ k₁:A₁, ..., kᵤ:Aᵤ } <: { l₁:B₁, ..., lᵤ:Bᵤ }
|
||||
|
||||
You will most likely need to prove inversion lemmas for the subtype relation
|
||||
of the form:
|
||||
|
||||
If S <: T₁ ⇒ T₂, then S ≡ S₁ ⇒ S₂, T₁ <: S₁, and S₂ <: T₂, for some S₁, S₂.
|
||||
|
||||
If S <: { lᵢ : Tᵢ | i ∈ 1..n }, then S ≡ { kⱼ : Sⱼ | j ∈ 1..m }
|
||||
and { lᵢ | i ∈ 1..n } ⊆ { kⱼ | j ∈ 1..m }
|
||||
and Sⱼ <: Tᵢ for every i and j such that lᵢ = kⱼ.
|
||||
|
||||
## References
|
||||
|
||||
* Reynolds 1980
|
||||
|
||||
* Cardelli 1984
|
||||
|
||||
* Liskov
|
||||
|
||||
* Types and Programming Languages. Benjamin C. Pierce. The MIT Press. 2002.
|
||||
|
|
Loading…
Reference in a new issue