10 TypeCheck
10 TypeCheck
Yuepeng Wang
Spring 2025
1
Overview
• Abstract semantics
• Type safety
2
Motivation
• (Operational) semantics is useful for proving some properties or
correctness of certain programs
• Example: prove "if x > 0 then x else -x" computes the absolute value of x
3
Abstract Semantics
• Even though we cannot prove some behavior of the original program, we
can still prove something over an abstraction of the program
4
Abstract Semantics
• The 3x+1 problem. Define a function f
result by
concrete semantics
• Example: positive integer does not over-approximate value -1, but integer
over-approximates -1
6
Abstract Semantics
• The abstract semantics is less precise than the concrete operational
semantics
• But if the abstract semantics is "precise enough" for the property that we
want to prove, then it is fine
• Example: if we know x > 10, then we know "if x>10 then x else -1"
evaluates to a positive number
• But we need to know the exact value of x to know the evaluation result of
"if x>10 then x else -1"
7
Abstract Semantics
• If the semantics is abstract enough, then checking properties of the
abstraction of the program is decidable
8
Types and Abstract Semantics
• A type represents a set of values
• Int represents integers within a range
• Bool represents {true, false}
• String represents all possible strings
• Syntactically, the expression can only use three constants: true, false, 0
11
Case Study: Operational Semantics
• Provide an operational semantics without environments
e ::= 'true' | 'false' | '0'
| 'succ' e | 'pred' e
| 'iszero' e
⊢ true ⇓ true ⊢ false ⇓ false ⊢0⇓0
| 'if' e 'then' e 'else' e
12
Case Study: Interpreter
• The operational semantics is closely related to an interpreter
Interpreter
eval :: Expr -> Maybe Value
Data type for values
eval (CBool b) = Just (VBool b)
data Value = VBool Bool
eval CZero = Just (VInt 0)
| VInt Int
eval (Succ e) = fmap inc (eval e)
deriving (Eq, Ord, Read, Show)
eval (Pred e) = fmap dec (eval e)
eval (IsZero e) = case (eval e) of
Auxiliary functions
Just (VInt 0) -> Just (VBool True)
inc :: Value -> Value
Just (VInt _) -> Just (VBool False)
inc (VInt x) = VInt (x + 1)
_ -> Nothing
dec :: Value -> Value
eval (ITE c t e) = case (eval c) of
dec (VInt x) = VInt (x - 1)
Just (VBool True) -> eval t
Just (VBool False) -> eval e
_ -> Nothing
13
Case Study: Typing Rules
• Provide typing rules without environments
e ::= 'true' | 'false' | '0'
| 'succ' e | 'pred' e
| 'iszero' e
⊢ true : Bool ⊢ false : Bool ⊢ 0 : Int
| 'if' e 'then' e 'else' e
17
Example: Simple Expressions
• Provide an operational semantics without environments
e ::= 'true' | 'false' | '0'
| 'succ' e | 'pred' e
| 'iszero' e
⊢ true ⇓ true ⊢ false ⇓ false ⊢0⇓0
| 'if' e 'then' e 'else' e
18
Example: Simple Expressions
• Provide typing rules without environments
e ::= 'true' | 'false' | '0' ⊢ true : Bool (T-True)
| 'succ' e | 'pred' e
| 'iszero' e ⊢ false : Bool (T-False)
| 'if' e 'then' e 'else' e
⊢ 0 : Int (T-Zero)
⊢ e : Int ⊢ e : Int
(T-Succ) (T-IsZero)
⊢ succ e : Int ⊢ iszero e : Bool
⊢ e : Int ⊢ e1 : Bool ⊢ e2 : T ⊢ e3 : T
(T-Pred) (T-ITE)
⊢ pred e : Int ⊢ if e1 then e2 else e3 : T
19
Proof Example
• Prove the type of "if iszero 0 then succ (succ 0) else pred 0" is Int with
respect to the typing rules
(T-Zero)
⊢ 0 : Int
(T-Zero) (T-Succ) (T-Zero)
⊢ 0 : Int ⊢ succ 0 : Int ⊢ 0 : Int
(T-IsZero) (T-Succ) (T-Pred)
⊢ iszero 0 : Bool ⊢ succ (succ 0) : Int ⊢ pred 0 : Int
(T-ITE)
⊢ if iszero 0 then succ (succ 0) else pred 0 : Int
20
Strategies to Compute Types
• What are strategies to compute types for more realistic languages?
21
Type Checking
• Type checking is the process of verifying the types are compatible with
certain constraints (e.g., programmer declarations)
22
Type Inference
• Type inference means the automatic detection of the type of an
expression in a program
23
The FUN Language
• Consider the FUN language again
e ::= c | b | x | '(' e ')'
| e '+' e | e '-' e | ... (arithmetic op)
| e '==' e | ... (logical op)
| 'if' e 'then' e 'else' e (conditional)
| 'lambda' x ':' t '.' e (fun abstraction)
| 'app' e e (fun application)
| 'let' x ':' t '=' e 'in' e (let binding)
t ::= 'Int' | 'Bool' | '(' t ')'
| t '->' t (fun type)
• Identifiers in function abstraction and let bindings are augmented with types
24
Types in the FUN Language
• Types in the FUN language is consistent with a small Haskell fragment
26
Operational Semantics of FUN
e ::= c | b | x | '(' e ')'
E ⊢ e1 ⇓ v1 E ⊢ e1 ⇓ v1
| e '+' e | e '-' e | ...
| e '==' e | ...
E ⊢ e2 ⇓ v2 E ⊢ e2 ⇓ v2
(E-Plus) (E-Minus)
| 'if' e 'then' e 'else' e E ⊢ e1 + e2 ⇓ v1 + v2 E ⊢ e1 − e2 ⇓ v1 − v2
| 'lambda' x ':' t '.' e
| 'app' e e E ⊢ e1 ⇓ v E ⊢ e2 ⇓ v E ⊢ e1 ⇓ v1 E ⊢ e2 ⇓ v2
| 'let' x ':' t '=' e 'in' e Int v or Bool v Int v1, v2 or Bool v1, v2
(E-Eq1)
t ::= 'Int' | 'Bool' | '(' t ')'
E ⊢ e1 == e2 ⇓ true v1 ≠ v2
(E-Eq2)
| t '->' t
E ⊢ e1 == e2 ⇓ false
Int c Bool b
(E-Int) (E-Bool) E ⊢ e1 ⇓ true E ⊢ e2 ⇓ v2
E⊢c⇓c E⊢b⇓b (E-ITE1)
E ⊢ if e1 then e2 else e3 ⇓ v2
Ident x
E(x) = v
(E-Ident) E ⊢ e1 ⇓ false E ⊢ e3 ⇓ v3
E⊢x⇓v (E-ITE2)
E ⊢ if e1 then e2 else e3 ⇓ v3
27
Operational Semantics of FUN
e ::= c | b | x | '(' e ')'
| e '+' e | e '-' e | ... (E-Abs)
| e '==' e | ...
E ⊢ lambda x : T . e ⇓ lambda x : T . e
| 'if' e 'then' e 'else' e
| 'lambda' x ':' t '.' e E ⊢ e1 ⇓ lambda x : T . e3
| 'app' e e E ⊢ e3[x ↦ e2] ⇓ v
| 'let' x ':' t '=' e 'in' e (E-App)
t ::= 'Int' | 'Bool' | '(' t ')' E ⊢ app e1 e2 ⇓ v
| t '->' t
E ⊢ e1 ⇓ v1
E[x ◃ v1] ⊢ e2 ⇓ v
(E-Let)
E ⊢ let x : T = e1 in e2 ⇓ v
28
Basic Typing Rules of FUN
e ::= c | b | x | '(' e ')'
| e '+' e | e '-' e | ... Int c Bool b
(T-Int) (T-Bool)
| e '==' e | ... Γ ⊢ c : Int Γ ⊢ b : Bool
| 'if' e 'then' e 'else' e
| 'lambda' x ':' t '.' e
Γ ⊢ e1 : Int Γ ⊢ e1 : Int
| 'app' e e
| 'let' x ':' t '=' e 'in' e Γ ⊢ e2 : Int Γ ⊢ e2 : Int
(T-Plus) (T-Minus)
t ::= 'Int' | 'Bool' | '(' t ')' Γ ⊢ e1 + e2 : Int Γ ⊢ e1 − e2 : Int
| t '->' t
• The type of integer constants is Int. The type of boolean constants is Bool
29
Basic Typing Rules of FUN
e ::= c | b | x | '(' e ')'
| e '+' e | e '-' e | ... Γ ⊢ e1 : T Γ ⊢ e2 : T T ∈ {Int, Bool}
(T-Eq)
| e '==' e | ...
Γ ⊢ e1 == e2 : Bool
| 'if' e 'then' e 'else' e
| 'lambda' x ':' t '.' e
| 'app' e e
Γ ⊢ e1 : Bool Γ ⊢ e2 : T Γ ⊢ e3 : T
| 'let' x ':' t '=' e 'in' e (T-ITE)
t ::= 'Int' | 'Bool' | '(' t ')' Γ ⊢ if e1 then e2 else e3 : T
| t '->' t
• If both e1 and e2 have type T (Int or Bool), then e1 == e2 have type Bool
• Note that we cannot compare two function abstractions
• If e1 has type Bool, e2 and e3 have type T (including function types), then "if
e1 then e2 else e3" has type T
30
Typing Rules for Abstractions
e ::= c | b | x | '(' e ')'
| e '+' e | e '-' e | ...
| e '==' e | ...
| 'if' e 'then' e 'else' e
| 'lambda' x ':' t '.' e
Γ[x ◃ T1] ⊢ e : T2
(T-Abs)
| 'app' e e Γ ⊢ lambda x : T1 . e : T1 → T2
| 'let' x ':' t '=' e 'in' e
t ::= 'Int' | 'Bool' | '(' t ')'
| t '->' t
• Since e can use variable x, we need to compute the type of e based on the
knowledge that the type of x is T1
31
Typing Environments
Γ[x ◃ T1] ⊢ e : T2
(T-Abs)
Γ ⊢ lambda x : T1 . e : T1 → T2
34
Proof Example
• Prove the type of "lambda x: Int. x + 1" is Int → Int
35
Proof Example
• Prove the type of "lambda x: Int. lambda y: Int. x + y" is Int → Int → Int
Ident x Γ[x ◃ Int][y ◃ Int](x) = Int Ident y Γ[x ◃ Int][y ◃ Int](y) = Int
(T-Ident) (T-Ident)
Γ[x ◃ Int][y ◃ Int] ⊢ x : Int Γ[x ◃ Int][y ◃ Int] ⊢ y : Int
(T-Plus)
Γ[x ◃ Int][y ◃ Int] ⊢ x + y : Int
(T-Abs)
Γ[x ◃ Int] ⊢ lambda y: Int. x + y : Int → Int
(T-Abs)
Γ ⊢ lambda x: Int. lambda y: Int. x + y : Int → Int → Int
36
Typing Rules for Applications
e ::= c | b | x | '(' e ')'
| e '+' e | e '-' e | ...
| e '==' e | ...
| 'if' e 'then' e 'else' e
Γ ⊢ e1 : T1 → T2
| 'lambda' x ':' t '.' e Γ ⊢ e2 : T1
| 'app' e e (T-App)
• For function application app e1 e2, the typing rule needs to check e1 has a
function type of the form T1 → T2, and the actual parameter e2 has type T1
37
Typing Rules for Let Bindings
e ::= c | b | x | '(' e ')'
| e '+' e | e '-' e | ...
| e '==' e | ...
| 'if' e 'then' e 'else' e
Γ[x ◃ T1] ⊢ e1 : T1
| 'lambda' x ':' t '.' e Γ[x ◃ T1] ⊢ e2 : T2
(T-Let)
| 'app' e e
| 'let' x ':' t '=' e 'in' e
Γ ⊢ let x : T1 = e1 in e2 : T2
t ::= 'Int' | 'Bool' | '(' t ')'
| t '->' t
• Given the type of x is T1, the rule needs to check the type of e1 is indeed T1
39
Typing Rules for Parenthesized Exprs
• Typically, the parentheses are handled in the parsing process
• After we obtain the tree representation (called abstract syntax tree, or
AST) of a program from the parser, the parentheses are gone
+ +
(1+2)+3 1+(2+3)
+ 3 1 +
1 2 2 3
40
Type Safety
• We collectively refer to a set of typing rules as a type system
• One of the most basic properties of a type system is safety (also called
soundness): well-typed programs do not go wrong
41
Safety = Progress + Preservation
• Type safety of a language can be proved in two steps
42
Safety = Progress + Preservation
• The proof methodology is usually structural induction
• Prove the property holds for base cases (e.g., constants)
• Assuming the the property holds for all sub-expressions, prove it holds
for recursive cases (e.g., if-then-else)