Type Correctness Type Correctness Contracts provide signature precondition











![Type Variable & Type Polymorphism • Type of identity function is [T -> T] Type Variable & Type Polymorphism • Type of identity function is [T -> T]](https://slidetodoc.com/presentation_image_h/f2a7b78a979ae9b06394c3ad1b85efd1/image-12.jpg)









![Examples {T 1=Number, T 2=[[Number -> T 3]} ° {T 3=Boolean, T 1=[T 2 Examples {T 1=Number, T 2=[[Number -> T 3]} ° {T 3=Boolean, T 1=[T 2](https://slidetodoc.com/presentation_image_h/f2a7b78a979ae9b06394c3ad1b85efd1/image-22.jpg)


![Unifier of Type Expressions • Another Example: – τ1 = [S*[Number->S] – τ2 = Unifier of Type Expressions • Another Example: – τ1 = [S*[Number->S] – τ2 =](https://slidetodoc.com/presentation_image_h/f2a7b78a979ae9b06394c3ad1b85efd1/image-25.jpg)

































![1. 2. 3. 4. 5. 6. {} |- =: [Num*Num -> Bool] {} |- 1. 2. 3. 4. 5. 6. {} |- =: [Num*Num -> Bool] {} |-](https://slidetodoc.com/presentation_image_h/f2a7b78a979ae9b06394c3ad1b85efd1/image-59.jpg)










![Done! The inferred type is: [[Number -> T 2] * [Number -> Number] -> Done! The inferred type is: [[Number -> T 2] * [Number -> Number] ->](https://slidetodoc.com/presentation_image_h/f2a7b78a979ae9b06394c3ad1b85efd1/image-70.jpg)
- Slides: 70
Type Correctness
Type Correctness • Contracts provide signature, precondition etc. • It says nothing about implementation • Program correctness deal with proving that implementation satisfies contract.
Program Correctness • Type correctness – Verify that the types of expressions in the program are “correct” – well-typing – E. g. + is applied to numbers – In other languages: we should also check that the type of value stored in a variable correspond to the variable declared type • Program Verification – Verify that the program halts and produces the “correct” output – Somewhat easier with design-by-contract, where it can be done in a modular fashion
Static/Dynamic Type Check • Static – Based on program code – can be applied off-line • Dynamic – Needs concrete data – done at runtime
Static Type Inference • We will see 2 algorithms: – Type inference rules – Type equations
Type Checking / Inference • Infer the type of an expression. • In other words, we associate an expression e with a type t such that the evaluation of e yields a value in t. • The purpose is to guarantee type safety.
Languages and Types How to include type information ? • Fully typed (every value has a type) – C, Pascal, Java. . • Semi-typed (allow typeless structures of values) – Scheme • Untyped – Prolog
How Types are Specified? – the Type Language • We need a language for writing types – Atomic types – Composite types – User defined types • Language expressions vs. Type expressions – Value constructors vs Type constructors • Type Polymorphism – Type variables
The Type Language • Our restricted type language: – Atomic types: Number, Boolean, Symbol, Void, Empty and Procedure. – No union types. – No user-defined types.
Type Variable & Type Polymorphism What is the type of: (lambda (x) x) ? It can applied to values of any type! ( (lambda (x) x) 3) => 3 ( (lambda (x) x) #t) => #t ( (lambda (x) x) +) => <primitive: +> [Number -> Number] [Boolean -> Boolean] [[Number * Number] -> Number]
Type Variable & Type Polymorphism • We know variables that hold values. Now we have variables that holds types! • Type variables provide the necessary abstraction: we can substitute (replace) type variables by other type expressions
Type Variable & Type Polymorphism • Type of identity function is [T -> T] • What is the type of: (lambda (f x)) [[T 1 -> T 2]*T 1 -> T 2] (lambda (f x) ( (f x) x)) [[T 1 -> T 2]]*T 1 -> T 2]
Type Variable & Type Polymorphism • Type expressions that include type variables are called polymorphic type expressions • Language expressions whose type is polymorphic are called polymorphic language expressions
Type Variable & Type Polymorphism • Unfortunately for you, checking well-typing with polymorphic types requires the concepts of: – Substitution – Composition of substitutions – Application of a substitution – Renaming
Type Variable & Type Polymorphism • Fortunately for you, you have already met those concepts • More fortunately for you, we will again go over the definitions! (with type flavor)
BNF of the Type Language Type -> ’Void’ | Non-void Non-Void -> Atomic | Composite | Type-variable Atomic -> ’Number’ | ’Boolean’ | ’Symbol’ Composite -> Procedure | Tuple Procedure -> ’[’ Tuple ’->’ Type ’]’ Tuple -> (Non-void ’*’ )* Non-void | ’Empty’ Pair -> ’Pair’ ’(’ Non-void ’, ’ Non-void ’)’ List -> ’List’ ’(’ Non-void ’)’ | ’List’ Type-variable -> A symbol
Value and Type Constructors • Value Constructor – Creates values! – Examples: 3, lambda • Type Constructor – Creates types! – Example: *, ->
Type Substitution Mapping s from a finite set of type variables to a finite set to type expressions, such that s(T) does not include T. {T 1=Number, T 2=[[Number -> T 3]} {T 1=Number, T 2=[[Number -> T 3] -> T 2]} illegal
Application of Type Substitution Type expression τ, type substitution s. τ °s = replacing all occurrences of type variable T in τ by s(T) [[T 1 -> T 2] ° {T 1=Number, T 2=[T 3 -> T 3]} = [[Number -> [T 3 -> T 3]]
Application of Type Substitution A type expression τ’ is an instance of typeexpression τ if there is a type-substitution s such that τ◦s= τ’. τ is more general than τ’ if τ’ is an instance of τ. e. g. all following are instances of [T -> T]: [Num -> Num] = [T->T]◦{T = Num} [Sym -> Sym] = [T->T]◦{T = Sym} [[Num -> Num] -> [Num -> Num]] = [T->T]◦{T = [Num->Num]} [[Num -> T 1] -> [Num -> T 1]] = [T->T]◦{T = [Num->T 1]}
Combination of Type Substitution Two type-substitution s 1, s 2 , s 1°s 2 is defined: 1. s 2 is applied to the type-expressions of s 1 2. A variable T for which s 1(T) is defined is removed from s 2 3. Modified s 2 is added to s 1 4. Identity bindings are removed. Relax! Examples on next slide
Examples {T 1=Number, T 2=[[Number -> T 3]} ° {T 3=Boolean, T 1=[T 2 -> T 2]} = {T 1 = Number, T 2 = [[Number -> Boolean]}
Renaming Type variables can be consistently renamed, but the new variables name should be new. [[T 1 -> T 2]*T 1 -> T 2] [[S 1 -> T 2]*S 1 -> T 2] [[S 1 -> S 2]*S 1 -> S 2] [[T 1 -> T 2]*S 2 -> T 2] [[T 2 -> T 2]*T 2 -> T 2] [[[T 1 -> T 2]*[T 1 -> T 2]
Unifier of Type Expressions • A type-substitution s, s. t. for two type expressions τ1, τ2 : τ1◦ s = τ2 ◦ s (no common type variables) • Example: – τ1 = [S*[Number->S 1]->S] – τ2 = [Pair(T 1)*[T 1 ->T 1]->T 2] – s = {S=Pair(Number), T 1=Number, S 1=Number, T 2=Pair(Number)}
Unifier of Type Expressions • Another Example: – τ1 = [S*[Number->S] – τ2 = [Pair(T 1)*[T 1 ->T 1]->T 2] –s=?
Most General Unifier • There can be infinite number of unifiers. For example: – τ1 = [S*S->S] – τ2 = [Pair(T 1)*T 2 ->T 2] – s= • • {S=Pair(T 1), T 2=Pair(T 1)} {S=Pair(Number), T 2=Pair(Number)} {S=Pair(Boolean), T 2=Pair(Boolean)} … • Only the first one is the most general unifier (mgu)
Static Type Inference for Scheme • We start with a typing system for a restricted language that includes only: – Atomic expressions with numbers, Booleans, primitive procedures, and variables – Composite expressions with quote forms, lambda forms and application forms
Terminology • Type Environment – A substitution mapping language variables to type expressions. – Is the set of variable type assumptions – Example: Tenv = {x: Number, y: [Number –> T]} – Notation: Tenv(x) = Number – Can be extended: {x: Number, y: [Number –> T]} ◦ {z: boolean} = {x: Number, y: [Number –> T], z: boolean} {} ◦ {x 1: T 1, . . . , xn: Tn} = {x 1: T 1, . . . , xn: Tn}
Terminology • Typing statement – A true/false formula – Tenv |- e: T – Under Tenv, the expression e has the type T • {x: Number} |- (+ x 5): Number • {x: [T 1 –> T 2], y: T 1} |- (x y): T 2
Terminology • Instantiation of Type statement – Applying substitution s to type statement TS – {x: [T 1 –> T 2]} |- (x e): T 2 – {x: [T 1 –> Boolean]} |- (x e): Boolean – {x: [Number –> Boolean]]} |(x e): [Number –> Boolean]
Terminology • Unifiable typing statements – Typing statements TS and TS’ are unifiable if there exists a type substitution s such that TS°s = TS’°s
Restricted Scheme (syntax) <scheme-exp> -> <exp> -> <atomic> | <composite> <atomic> -> <number> | <boolean> | <variable> <composite> -> <special> | <form> <number> -> Numbers <boolean> -> ’#t’ | ’#f’ <variable> -> sequences of letters <special> -> <lambda> | <quote> <form> -> ’(’ <exp>+ ’)’ <lambda> -> ’(’ ’lambda’ ’(’ <variable>* ’)’ <exp>+ ’)’ <quote> -> ’(’ ’quote’ <variable> ’)’ For now: no ‘if’s, no ‘define’s and no recursive procedures
Well-Typing Rules • To infer the type of an expression (as well as whether or not it is well-typed), we need to define axioms and rules • To be used on sub-expressions, to derive types of the more complex expression • Only abbreviated forms are given here, see lecture notes for full description of rules.
Notes • • • Meta variables Axiom and rule independence Identifying pattern The monotonicity rule Exhaustive sub-expression typing We’re almost ready to see the algorithm…
Expression Trees The nesting of expressions can be viewed as a tree Sub-trees correspond to composite expressions For lambda expressions, their body expressions reside in their children Leaves correspond to atomic ones
Expression Tree Example Tree for (+ 2 (+ 5 7)) + 2 (+ 5 7) + 5 7
Type Derivation (inference) Algorithm Main idea: go bottom-up on the expression tree, deriving new type statements by using the “typestatements-pool”, rules and substitution Add the result to the type-statements-pool Declare T if you get {} |-e: T FAIL otherwise
Example Derive a typing statement for (+ 2 ( + 5 7)) (+ 2 (+ 5 7)) + 2 (+ 5 7) + 5 7
We start with the leaves: we use Number and primitives axioms. 1. 2. 3. 4. { { } } ||||- 5: Number 7: Number 2: Number +: [Number*Number -> Number]
Next we deal with (+ 5 7): Application axiom. For every: type environment _Tenv, expressions _f, _e 1, . . . , _en, n >= 0 , and type expressions _S 1, . . . , _Sn, _S: Procedure with parameters (n > 0): If _Tenv |- _f: [_S 1*. . . *_Sn -> _S], _Tenv |- _e 1: _S 1, . . . , _Tenv |- _en: _Sn Then _Tenv |- (_f _e 1. . . _en): _S Application of typing rule Application to typing statements 4, 1, 2, with type substitution {_S 1=Number, _S 2=Number, _S=Number}: 5. { } |- (+ 5 7): Number
Application of typing rule Application to typing statements 4, 3, 5, with type substitution {_S 1=Number, _S 2=Number, _S=Number}: 6. { } |- (+ 2 (+ 5 7)): Number Done
Type-Derivation Algorithm
For Every Type Environment… • Non determinism • So: always pick a minimal type environment: – Axioms user empty TE, except Variable axiom. – Procedure rule TE only with free variables – Application rule TE of existing TS
Well-typeness • e is well-typed if Type-derivation(e) does not fail. • e has a type if it is well typed and Typederivation(e) ends with { } |- e: t
1. 2. 3. 4. 5. 6. { } |- 5: Number { } |- 3: Number { } |- +: [Number*Number -> Number] {x: T} |- x: T (variable axiom) {x: T 1} |- 3: Number (monoticity) {x: T 2} |- +: [Number*Number -> Number] (monoticity) 7. {x: Number} |- (+ x 3): Number (application, unification) 8. { } |- (lambda (x) (+ x 3)): [Number -> Number] (procedure) 9. { } |- ((lambda (x) (+ x 3)) 5): Number (application)
Another Example: Fail
1. 2. 3. 4. 5. {x 1: T 1} |- x 1: T 1 { } |- +: [Number*Number -> Number] {x: T 2} |- x: T 2 { } |- (lambda (x 1): [T 1 -> T 1] Fail
Definitions > (define x 1) > (define y (+ x 1)) > (+ x y) 3 What is the type of x, y, (+ x y) ?
Typing Rule Define For every definition expression (define _x _e) and type expression _S: If { } |- _e: _S, Then { } |- _x: _S
Example Given > (define x 1) > (define y (+ x 1)) Derive a type for (+ x y)
1. { } |- 1 : Number 2. { } |- x : Number 3. { } |- + : [Number * Number] > Number 4. { } |- (+ x 1) : Number 5. { } |- y: Number 6. { } |- (+ x y): Number
Throwing in control flow (if) For every type environment _Tenv, expressions _e 1, _e 2, _e 3, and type expressions _S 1, _S 2: If _Tenv |- _e 1: S 1 and _Tenv |- _e 2: _S 2 and _Tenv |- _e 3: _S 2 Then _Tenv |- (if _e 1 _e 2 _e 3) : _S 2
Recursions • (define f e), but e contains a free variable • We modify the notion of well-typeness: – (define f e) is ok if: {f: [S 1*. . . Sn –> S]} |- e: [S 1*. . . Sn –> S]
Recursions For every: recursive-definition expression (define _f _e) and type expression _S: If {_f: _S } |- _e: _S, Then { } |- (define _f _e): Void, and { } |- _f: _S
Example (define fact (λ (n) (if (= n 0) 1 (* n (fact (- n 1))))))
1. 2. 3. 4. 5. 6. {} |- =: [Num*Num -> Bool] {} |- -: [Num*Num -> Num] {} |- *: [Num*Num -> Num] {} |- 1: Num {n: T 1} |- n: T 1 {fact: T 2} |- fact: T 2 Application rule on ts 2, 4, 5 {_S 1=T 1=Num, _S 2=Num, _S=num} 7. {n: Num} |- (- n 1): num Application rule on ts 6, 7 {_S 1=Num, _S=T 3, T 2=[Num->T 3]} 8. {fact: [Num->T 3], n: Num} |(fact (- n 1)): T 3 Application rule on ts 5, 6, 8 {_S 1=Num, _S 2=Num, _S=Num, T 1=Num, T 3=Num} 9. {fact: [Num->Num], n: Num} |(* n (fact (- n 1))): Num Application rule on ts 1, 5, 4 {_S 1=Bool, _S 2=Num, _S=Bool} 10. {n: Num} |- (= n 0): Bool if rule on ts 4, 9, 10 {_S 1=Bool, _S 2=Num, _S=Bool} 11. {fact: [Num->Num], n: Num} |(if …): Num Procedure rule on ts 11 {_S 1=Num, _U 1=Num} 12. {fact: [Num->Num]} |(λ (n) (if …)): [Num -> Num] Recursive-definition to statement no. 12, with type-substitution {_S=[Number -> Number]}: 13. { } |- (define fact (λ (n) (if …))): Void 14. { } |- fact: [Num -> Num] Well typed!
Pair & List Types • Operations are primitives, so all have axioms.
Type Constraints Approach 4 Stages: I. Rename bound variables II. Assign type variable for all sub-expression III. Construct type equations over these variables – Numbers, booleans, quoted symbols, primitive procedures: Construct equations using their types. – Intuitive rules for lambda and applications IV. Solve the equations
Demo by Example (λ (f g) (λ (x) (f (+ x (g 3)))))
Stage II: Assign Type Variables Expression Variable (lambda (f g) (lambda (x) (f (+ x (g 3))))) T 0 (lambda (x) (f (+ x (g 3)))) T 1 (f (+ x (g 3))) T 2 f Tf (+ x (g 3)) T 3 + T+ x Tx (g 3) T 4 g Tg 3 Tnum 3
Stage III: Construct Type Equations • Primitives: use the primitive type • λ expression: for (λ (v 1…vn) e 1…em) contruct: T(λ (v 1…vn) e 1…em)=[Tv 1*…*Tvn -> Tem] • Application: for (f e 1 … en) construct: Tf=[Te 1*…*Ten -> T(f e 1 … en)] On board
Solving the equations 1. Apply current substitution on equation 2. Check atomic types 3. If a circular substitution is created output FAIL. 4. If both sides of equation are composite and have same type constructor, split the equation into equations between corresponding components
Done! The inferred type is: [[Number -> T 2] * [Number -> Number] -> [Number -> T 2]]