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 AThe 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! 🐙)