In this example, we build a certified type checker for a simple expression language.
Remark: this example is based on an example in the book [Certified Programming with Dependent Types](http://adam.chlipala.net/cpdt/) by Adam Chlipala.
inductive Expr: Type
Expr where
| nat: Nat → _root_.Expr
nat : Nat: Type
Nat → Expr: Sort _tmp_ind_univ_param
Expr
| plus: _root_.Expr → _root_.Expr → _root_.Expr
plus : Expr: Sort _tmp_ind_univ_param
Expr → Expr: Sort _tmp_ind_univ_param
Expr → Expr: Sort _tmp_ind_univ_param
Expr
| bool: Bool → _root_.Expr
bool : Bool: Type
Bool → Expr: Sort _tmp_ind_univ_param
Expr
| and: _root_.Expr → _root_.Expr → _root_.Expr
and : Expr: Sort _tmp_ind_univ_param
Expr → Expr: Sort _tmp_ind_univ_param
Expr → Expr: Sort _tmp_ind_univ_param
Expr
We define a simple language of types using the inductive datatype Ty
, and
its typing rules using the inductive predicate HasType
.
inductive Ty: Type
Ty where
| nat: _root_.Ty
nat
| bool: _root_.Ty
bool
deriving DecidableEq: Sort u → Sort (max 1 u)
DecidableEq
inductive HasType: Expr → Ty → Prop
HasType : Expr: Type
Expr → Ty: Type
Ty → Prop: Type
Prop
| nat: ∀ {v : Nat}, _root_.HasType (Expr.nat v) Ty.nat
nat : HasType: Expr → Ty → Prop
HasType (.nat: Nat → Expr
.nat v: Nat
v) .nat: Ty
.nat
| plus: ∀ {a b : Expr}, _root_.HasType a Ty.nat → _root_.HasType b Ty.nat → _root_.HasType (Expr.plus a b) Ty.nat
plus : HasType: Expr → Ty → Prop
HasType a: Expr
a .nat: Ty
.nat → HasType: Expr → Ty → Prop
HasType b: Expr
b .nat: Ty
.nat → HasType: Expr → Ty → Prop
HasType (.plus: Expr → Expr → Expr
.plus a: Expr
a b: Expr
b) .nat: Ty
.nat
| bool: ∀ {v : Bool}, _root_.HasType (Expr.bool v) Ty.bool
bool : HasType: Expr → Ty → Prop
HasType (.bool: Bool → Expr
.bool v: Bool
v) .bool: Ty
.bool
| and: ∀ {a b : Expr}, _root_.HasType a Ty.bool → _root_.HasType b Ty.bool → _root_.HasType (Expr.and a b) Ty.bool
and : HasType: Expr → Ty → Prop
HasType a: Expr
a .bool: Ty
.bool → HasType: Expr → Ty → Prop
HasType b: Expr
b .bool: Ty
.bool → HasType: Expr → Ty → Prop
HasType (.and: Expr → Expr → Expr
.and a: Expr
a b: Expr
b) .bool: Ty
.bool
We can easily show that if e
has type t₁
and type t₂
, then t₁
and t₂
must be equal
by using the the cases
tactic. This tactic creates a new subgoal for every constructor,
and automatically discharges unreachable cases. The tactic combinator tac₁ <;> tac₂
applies
tac₂
to each subgoal produced by tac₁
. Then, the tactic rfl
is used to close all produced
goals using reflexivity.
theoremHasType.detHasType.det: ∀ {e : Expr} {t₁ t₂ : Ty}, HasType e t₁ → HasType e t₂ → t₁ = t₂(h₁h₁: HasType e t₁:HasTypeHasType: Expr → Ty → Propee: Exprt₁t₁: Ty) (h₂h₂: HasType e t₂:HasTypeHasType: Expr → Ty → Propee: Exprt₂t₂: Ty) :t₁t₁: Ty=t₂t₂: Ty:=e: Expr
t₁, t₂: Ty
h₁: HasType e t₁
h₂: HasType e t₂t₁ = t₂t₂: Ty
v✝: Nat
h₂: HasType (Expr.nat v✝) t₂
natTy.nat = t₂t₂: Ty
a✝², b✝: Expr
h₂: HasType (Expr.plus a✝² b✝) t₂Ty.nat = t₂t₂: Ty
v✝: Bool
h₂: HasType (Expr.bool v✝) t₂Ty.bool = t₂t₂: Ty
a✝², b✝: Expr
h₂: HasType (Expr.and a✝² b✝) t₂Ty.bool = t₂e: Expr
t₁, t₂: Ty
h₁: HasType e t₁
h₂: HasType e t₂t₁ = t₂
bool.boolTy.bool = Ty.boolt₂: Ty
a✝², b✝: Expr
h₂: HasType (Expr.plus a✝² b✝) t₂
plusTy.nat = t₂Goals accomplished! 🐙
The inductive type Maybe p
has two contructors: found a h
and unknown
.
The former contains an element a : α
and a proof that a
satisfies the predicate p
.
The constructor unknown
is used to encode "failure".
inductive Maybe: {α : Sort u_1} → (α → Prop) → Sort (max 1 u_1)
Maybe (p: α → Prop
p : α: Sort u_1
α → Prop: Type
Prop) where
| found: {α : Sort u_1} → {p : α → Prop} → (a : α) → p a → _root_.Maybe p
found : (a: α
a : α: Sort u_1
α) → p: α → Prop
p a: α
a → Maybe: {α : Sort u_1} → (α → Prop) → Sort _tmp_ind_univ_param
Maybe p: α → Prop
p
| unknown: {α : Sort u_1} → {p : α → Prop} → _root_.Maybe p
unknown
We define a notation for Maybe
that is similar to the builtin notation for the Lean builtin type Subtype
.
notation "{{ " x: Lean.Syntax
x " | " p: Lean.Syntax
p " }}" => Maybe (fun x: Lean.Syntax
x => p: Lean.Syntax
p)
The function Expr.typeCheck e
returns a type ty
and a proof that e
has type ty
,
or unknown
.
Recall that, def Expr.typeCheck ...
in Lean is notation for namespace Expr def typeCheck ... end Expr
.
The term .found .nat .nat
is sugar for Maybe.found Ty.nat HasType.nat
. Lean can infer the namespaces using
the expected types.
def Expr.typeCheck: (e : Expr) → Maybe fun ty => HasType e ty
Expr.typeCheck (e: Expr
e : Expr: Type
Expr) : {{ ty: Ty
ty | HasType: Expr → Ty → Prop
HasType e: Expr
e ty: Ty
ty }} :=
match e: Expr
e with
| nat: Nat → Expr
nat .. => .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .nat: Ty
.nat .nat: ∀ {v : Nat}, HasType (nat v) Ty.nat
.nat
| bool: Bool → Expr
bool .. => .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .bool: Ty
.bool .bool: ∀ {v : Bool}, HasType (bool v) Ty.bool
.bool
| plus: Expr → Expr → Expr
plus a: Expr
a b: Expr
b =>
match a: Expr
a.typeCheck: (e : Expr) → Maybe fun ty => HasType e ty
typeCheck, b: Expr
b.typeCheck: (e : Expr) → Maybe fun ty => HasType e ty
typeCheck with
| .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .nat: Ty
.nat h₁: HasType a Ty.nat
h₁, .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .nat: Ty
.nat h₂: HasType b Ty.nat
h₂ => .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .nat: Ty
.nat (.plus: ∀ {a b : Expr}, HasType a Ty.nat → HasType b Ty.nat → HasType (plus a b) Ty.nat
.plus h₁: HasType a Ty.nat
h₁ h₂: HasType b Ty.nat
h₂)
| _: Maybe fun ty => HasType a ty
_, _: Maybe fun ty => HasType b ty
_ => .unknown: {α : Type} → {p : α → Prop} → Maybe p
.unknown
| and: Expr → Expr → Expr
and a: Expr
a b: Expr
b =>
match a: Expr
a.typeCheck: (e : Expr) → Maybe fun ty => HasType e ty
typeCheck, b: Expr
b.typeCheck: (e : Expr) → Maybe fun ty => HasType e ty
typeCheck with
| .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .bool: Ty
.bool h₁: HasType a Ty.bool
h₁, .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .bool: Ty
.bool h₂: HasType b Ty.bool
h₂ => .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found .bool: Ty
.bool (.and: ∀ {a b : Expr}, HasType a Ty.bool → HasType b Ty.bool → HasType (and a b) Ty.bool
.and h₁: HasType a Ty.bool
h₁ h₂: HasType b Ty.bool
h₂)
| _: Maybe fun ty => HasType a ty
_, _: Maybe fun ty => HasType b ty
_ => .unknown: {α : Type} → {p : α → Prop} → Maybe p
.unknown
-- TODO: for simplifying the following proof we need: ematching for forward reasoning, and `match` blast for case analysis
Now, we prove that if Expr.typeCheck e
returns Maybe.unknown
, then forall ty
, HasType e ty
does not hold.
The notation e.typeCheck
is sugar for Expr.typeCheck e
. Lean can infer this because we explicitly said that e
has type Expr
.
The proof is by induction on e
and case analysis. The tactic rename_i
is used to to rename "inaccessible" variables.
We say a variable is inaccessible if it is introduced by a tactic (e.g., cases
) or has been shadowed by another variable introduced
by the user. Note that the tactic simp [typeCheck]
is applied to all goal generated by the induction
tactic, and closes
the cases corresponding to the constructors Expr.nat
and Expr.bool
.
theoremExpr.typeCheck_completeExpr.typeCheck_complete: ∀ {ty : Ty} {e : Expr}, typeCheck e = Maybe.unknown → ¬HasType e ty{ee: Expr:ExprExpr: Type} :ee: Expr.typeChecktypeCheck: (e : Expr) → Maybe fun ty => HasType e ty=.unknown.unknown: {α : Type} → {p : α → Prop} → Maybe p→ ¬HasTypeHasType: Expr → Ty → Propee: Exprtyty: Ty:=ty: Ty
e: ExprtypeCheck e = Maybe.unknown → ¬HasType e tyty: Ty
e: ExprtypeCheck e = Maybe.unknown → ¬HasType e tyty: Ty
a, b: Expr
iha: typeCheck a = Maybe.unknown → ¬HasType a ty
ihb: typeCheck b = Maybe.unknown → ¬HasType b ty
plus(match typeCheck a, typeCheck b with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
iha: typeCheck a = Maybe.unknown → ¬HasType a ty
ihb: typeCheck b = Maybe.unknown → ¬HasType b ty
plus(match typeCheck a, typeCheck b with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
plus(typeCheck a = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match typeCheck a, typeCheck b with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType a a✝¹
plus.found(Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match Maybe.found a✝¹ a✝, typeCheck b with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr(Maybe.unknown = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match Maybe.unknown, typeCheck b with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
plus(typeCheck a = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match typeCheck a, typeCheck b with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType b a✝¹
plus.unknown.found(Maybe.unknown = Maybe.unknown → ¬HasType a ty) → (Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType b ty) → (match Maybe.unknown, Maybe.found a✝¹ a✝ with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr(Maybe.unknown = Maybe.unknown → ¬HasType a ty) → (Maybe.unknown = Maybe.unknown → ¬HasType b ty) → (match Maybe.unknown, Maybe.unknown with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType a a✝¹
plus.found(Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match Maybe.found a✝¹ a✝, typeCheck b with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType b a✝¹
plus.unknown.found¬HasType a ty → ¬HasType (plus a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType a a✝¹
plus.found.unknown(Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType a ty) → (Maybe.unknown = Maybe.unknown → ¬HasType b ty) → (match Maybe.found a✝¹ a✝, Maybe.unknown with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
plus.unknown.unknown¬HasType (plus a b) tyty: Ty
a, b: Expr
a✝³: Ty
a✝²: HasType a a✝³
a✝¹: Ty
a✝: HasType b a✝¹
plus.found.found(match Maybe.found a✝³ a✝², Maybe.found a✝¹ a✝ with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (plus a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType b a✝¹
h: HasType (plus a b) ty
plus.unknown.foundFalsety: Ty
a, b: Expr
plus.unknown.unknown¬HasType (plus a b) tya, b: Expr
a✝³: Ty
a✝²: HasType b a✝³
plus.unknown.found.plusFalsety: Ty
a, b: Expr
a✝²: Ty
a✝¹: HasType a a✝²
a✝: (match Maybe.found a✝² a✝¹, Maybe.unknown with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown
h: HasType (plus a b) ty
plus.found.unknownFalsea, b: Expr
plus.unknown.unknown.plusFalseGoals accomplished! 🐙a, b: Expr
ty₁: Ty
a✝³: HasType a ty₁
ty₂: Ty
a✝²: HasType b ty₂
h: (match Maybe.found ty₁ a✝³, Maybe.found ty₂ a✝² with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown
plus.found.found.plusFalsea, b: Expr
ty₂: Ty
a✝³: HasType b ty₂
a✝: HasType a Ty.nat
h: (match Maybe.found Ty.nat a✝, Maybe.found ty₂ a✝³ with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown
plus.found.found.plus.natFalsea, b: Expr
ty₂: Ty
a✝³: HasType b ty₂
a✝: HasType a Ty.bool
h: (match Maybe.found Ty.bool a✝, Maybe.found ty₂ a✝³ with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknownFalsea, b: Expr
ty₁: Ty
a✝³: HasType a ty₁
ty₂: Ty
a✝²: HasType b ty₂
h: (match Maybe.found ty₁ a✝³, Maybe.found ty₂ a✝² with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown
plus.found.found.plusFalsea, b: Expr
a✝¹: HasType a Ty.nat
a✝: HasType b Ty.nat
h: (match Maybe.found Ty.nat a✝¹, Maybe.found Ty.nat a✝ with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown
plus.found.found.plus.nat.natFalsea, b: Expr
a✝¹: HasType a Ty.nat
a✝: HasType b Ty.bool
h: (match Maybe.found Ty.nat a✝¹, Maybe.found Ty.bool a✝ with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknownFalsea, b: Expr
ty₂: Ty
a✝³: HasType b ty₂
a✝: HasType a Ty.nat
h: (match Maybe.found Ty.nat a✝, Maybe.found ty₂ a✝³ with | Maybe.found Ty.nat h₁, Maybe.found Ty.nat h₂ => Maybe.found Ty.nat (_ : HasType (plus a b) Ty.nat) | x, x_1 => Maybe.unknown) = Maybe.unknown
plus.found.found.plus.natFalsea, b: Expr
h: True
plus.found.found.plus.nat.boolFalsea, b: Expr
h: True
plus.found.found.plus.nat.boolFalsea, b: Expr
h: True
this: Ty.bool = Ty.nat
plus.found.found.plus.nat.boolFalse;Goals accomplished! 🐙a, b: Expr
h: True
plus.found.found.plus.bool.natFalsea, b: Expr
h: True
this: Ty.bool = Ty.nat
plus.found.found.plus.bool.natFalse;Goals accomplished! 🐙a, b: Expr
h: True
plus.found.found.plus.bool.boolFalsea, b: Expr
h: True
this: Ty.bool = Ty.nat
plus.found.found.plus.bool.boolFalse;Goals accomplished! 🐙ty: Ty
a, b: Expr
iha: typeCheck a = Maybe.unknown → ¬HasType a ty
ihb: typeCheck b = Maybe.unknown → ¬HasType b ty
and(match typeCheck a, typeCheck b with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr
and(typeCheck a = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match typeCheck a, typeCheck b with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType a a✝¹
and.found(Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match Maybe.found a✝¹ a✝, typeCheck b with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr(Maybe.unknown = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match Maybe.unknown, typeCheck b with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr
and(typeCheck a = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match typeCheck a, typeCheck b with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType b a✝¹
and.unknown.found(Maybe.unknown = Maybe.unknown → ¬HasType a ty) → (Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType b ty) → (match Maybe.unknown, Maybe.found a✝¹ a✝ with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr(Maybe.unknown = Maybe.unknown → ¬HasType a ty) → (Maybe.unknown = Maybe.unknown → ¬HasType b ty) → (match Maybe.unknown, Maybe.unknown with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType a a✝¹
and.found(Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType a ty) → (typeCheck b = Maybe.unknown → ¬HasType b ty) → (match Maybe.found a✝¹ a✝, typeCheck b with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType b a✝¹
and.unknown.found¬HasType a ty → ¬HasType (and a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType a a✝¹
and.found.unknown(Maybe.found a✝¹ a✝ = Maybe.unknown → ¬HasType a ty) → (Maybe.unknown = Maybe.unknown → ¬HasType b ty) → (match Maybe.found a✝¹ a✝, Maybe.unknown with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown → ¬HasType (and a b) tyty: Ty
a, b: Expr
and.unknown.unknown¬HasType (and a b) tyty: Ty
a, b: Expr
a✝¹: Ty
a✝: HasType b a✝¹
and.unknown.found¬HasType a ty → ¬HasType (and a b) tyty: Ty
a, b: Expr
h: HasType (and a b) ty
and.unknown.unknownFalsety: Ty
a, b: Expr
and.unknown.unknown¬HasType (and a b) tya, b: Expr
and.unknown.unknown.andFalsety: Ty
a, b: Expr
a✝²: Ty
a✝¹: HasType a a✝²
a✝: (match Maybe.found a✝² a✝¹, Maybe.unknown with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown
h: HasType (and a b) ty
and.found.unknownFalsea, b: Expr
a✝³: Ty
a✝²: HasType b a✝³
and.unknown.found.andFalseGoals accomplished! 🐙a, b: Expr
ty₁: Ty
a✝³: HasType a ty₁
ty₂: Ty
a✝²: HasType b ty₂
h: (match Maybe.found ty₁ a✝³, Maybe.found ty₂ a✝² with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown
and.found.found.andFalsea, b: Expr
ty₂: Ty
a✝³: HasType b ty₂
a✝: HasType a Ty.nat
h: (match Maybe.found Ty.nat a✝, Maybe.found ty₂ a✝³ with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown
and.found.found.and.natFalsea, b: Expr
ty₂: Ty
a✝³: HasType b ty₂
a✝: HasType a Ty.bool
h: (match Maybe.found Ty.bool a✝, Maybe.found ty₂ a✝³ with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknownFalsea, b: Expr
ty₁: Ty
a✝³: HasType a ty₁
ty₂: Ty
a✝²: HasType b ty₂
h: (match Maybe.found ty₁ a✝³, Maybe.found ty₂ a✝² with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown
and.found.found.andFalsea, b: Expr
a✝¹: HasType a Ty.bool
a✝: HasType b Ty.nat
h: (match Maybe.found Ty.bool a✝¹, Maybe.found Ty.nat a✝ with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown
and.found.found.and.bool.natFalsea, b: Expr
a✝¹: HasType a Ty.bool
a✝: HasType b Ty.bool
h: (match Maybe.found Ty.bool a✝¹, Maybe.found Ty.bool a✝ with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknownFalsea, b: Expr
ty₂: Ty
a✝³: HasType b ty₂
a✝: HasType a Ty.bool
h: (match Maybe.found Ty.bool a✝, Maybe.found ty₂ a✝³ with | Maybe.found Ty.bool h₁, Maybe.found Ty.bool h₂ => Maybe.found Ty.bool (_ : HasType (and a b) Ty.bool) | x, x_1 => Maybe.unknown) = Maybe.unknown
and.found.found.and.boolFalseGoals accomplished! 🐙a, b: Expr
h: True
and.found.found.and.nat.natFalsea, b: Expr
h: True
this: Ty.bool = Ty.nat
and.found.found.and.nat.natFalse;Goals accomplished! 🐙a, b: Expr
h: True
and.found.found.and.nat.boolFalsea, b: Expr
h: True
this: Ty.bool = Ty.nat
and.found.found.and.nat.boolFalse;Goals accomplished! 🐙a, b: Expr
h: True
and.found.found.and.bool.natFalsea, b: Expr
h: True
this: Ty.bool = Ty.nat
and.found.found.and.bool.natFalse;Goals accomplished! 🐙
Finally, we show that type checking for e
can be decided using Expr.typeCheck
.
instance: (e : Expr) → (t : Ty) → Decidable (HasType e t)
instance (e: Expr
e : Expr: Type
Expr) (t: Ty
t : Ty: Type
Ty) : Decidable: Prop → Type
Decidable (HasType: Expr → Ty → Prop
HasType e: Expr
e t: Ty
t) :=
match h': Expr.typeCheck e = Maybe.unknown
h' : e: Expr
e.typeCheck: (e : Expr) → Maybe fun ty => HasType e ty
typeCheck with
| .found: {α : Type} → {p : α → Prop} → (a : α) → p a → Maybe p
.found t': Ty
t' ht': HasType e t'
ht' =>
if heq: ¬t = t'
heq : t: Ty
t = t': Ty
t' then
isTrue: {p : Prop} → p → Decidable p
isTrue (heq: t = t'
heq ▸ ht': HasType e t'
ht')
else
isFalse: {p : Prop} → ¬p → Decidable p
isFalse fun ht: HasType e t
ht => heq: ¬t = t'
heq (HasType.det: ∀ {e : Expr} {t₁ t₂ : Ty}, HasType e t₁ → HasType e t₂ → t₁ = t₂
HasType.det ht: HasType e t
ht ht': HasType e t'
ht')
| .unknown: {α : Type} → {p : α → Prop} → Maybe p
.unknown => isFalse: {p : Prop} → ¬p → Decidable p
isFalse (Expr.typeCheck_complete: ∀ {ty : Ty} {e : Expr}, Expr.typeCheck e = Maybe.unknown → ¬HasType e ty
Expr.typeCheck_complete h': Expr.typeCheck e = Maybe.unknown
h')