Lambda Calculus in Lean

3.3. Typing Judgment🔗

The judgment Γ ⊢ e : A is defined by the three usual rules: variables are typed by lookup, lambdas introduce function types, and applications eliminate function types.

\frac{x : A \in \Gamma}{\Gamma \vdash x : A} \qquad \frac{\Gamma, x : A \vdash e : B}{\Gamma \vdash \lambda x.\ e : A \to B}

\frac{\Gamma \vdash e_1 : A \to B \qquad \Gamma \vdash e_2 : A} {\Gamma \vdash e_1\ e_2 : B}

The inductive judgment below is exactly these three rules. Proofs of typing judgments are therefore derivation trees built from var, lam, and app.

inductive HasType : Context -> L1 -> Ty -> Prop where | var : lookup x Γ = some A -> HasType Γ (.var x) A | lam : HasType ((x, A) :: Γ) body B -> HasType Γ (.lam x body) (.arr A B) | app : HasType Γ fn (.arr A B) -> HasType Γ arg A -> HasType Γ (.app fn arg) Bnotation:50 Γ " ⊢ " t " : " A => HasType Γ t A

The first examples are derivations of simple typing judgments. They are small enough that the proof scripts follow the inference rules directly.

example : [("x", #A)] L1[x] : #A := [("x", Ty.base "A")] L1.var "x" : Ty.base "A" apply HasType.var (lookup "x" [("x", Ty.base "A")] = some (Ty.base "A") All goals completed! 🐙)example : [] L1.I : #A #A := [] L1.I : Ty.base "A" Ty.base "A" [] L1.lam "x" (L1.var "x") : Ty.base "A" Ty.base "A" [("x", Ty.base "A")] L1.var "x" : Ty.base "A" apply HasType.var (lookup "x" [("x", Ty.base "A")] = some (Ty.base "A") All goals completed! 🐙)example : [] L1.T : #A #B #A := [] L1.T : Ty.base "A" Ty.base "B" Ty.base "A" [] L1.lam "x" (L1.lam "y" (L1.var "x")) : Ty.base "A" Ty.base "B" Ty.base "A" [("x", Ty.base "A")] L1.lam "y" (L1.var "x") : Ty.base "B" Ty.base "A" [("y", Ty.base "B"), ("x", Ty.base "A")] L1.var "x" : Ty.base "A" apply HasType.var (lookup "x" [("y", Ty.base "B"), ("x", Ty.base "A")] = some (Ty.base "A") All goals completed! 🐙)example : [] L1.F : #A #B #B := [] L1.F : Ty.base "A" Ty.base "B" Ty.base "B" [] L1.lam "x" (L1.lam "y" (L1.var "y")) : Ty.base "A" Ty.base "B" Ty.base "B" [("x", Ty.base "A")] L1.lam "y" (L1.var "y") : Ty.base "B" Ty.base "B" [("y", Ty.base "B"), ("x", Ty.base "A")] L1.var "y" : Ty.base "B" apply HasType.var (lookup "y" [("y", Ty.base "B"), ("x", Ty.base "A")] = some (Ty.base "B") All goals completed! 🐙)example : [("y", #A)] L1[(λx. x) y] : #A := [("y", Ty.base "A")] (L1.lam "x" (L1.var "x")).app (L1.var "y") : Ty.base "A" [("y", Ty.base "A")] L1.lam "x" (L1.var "x") : Ty.base "A" Ty.base "A"[("y", Ty.base "A")] L1.var "y" : Ty.base "A" [("y", Ty.base "A")] L1.lam "x" (L1.var "x") : Ty.base "A" Ty.base "A" [("x", Ty.base "A"), ("y", Ty.base "A")] L1.var "x" : Ty.base "A" apply HasType.var (lookup "x" [("x", Ty.base "A"), ("y", Ty.base "A")] = some (Ty.base "A") All goals completed! 🐙) [("y", Ty.base "A")] L1.var "y" : Ty.base "A" apply HasType.var (lookup "y" [("y", Ty.base "A")] = some (Ty.base "A") All goals completed! 🐙)