TYPE CHECKING VS. TYPE INFERENCE Type Checking: Some declarations built-in (+, *, ...) User declares types (of names) Compiler infers types (of expressions) Compiler compares each inferred use to declared - with == in non-OO language - with <= in OO language (subtyping) Type Inference Some declarations built-in (+, *, ...) Compiler infers types (of expressions) Compiler compares all inferred uses - with == in non-OO language - with <= in OO language (subtyping) TYPE CHECKING NOTATION: JUDGEMENTS G |- x : T means given/assuming type envrionment G, one can prove that x has type T TYPE CHECKING NOTATION: AXIOMS AND INFERENCE RULES [var] G |- x : T if G(x) = T G |- E : T [asgn] ------------------- if G(x) = T G |- x := E : ok ENVIRONMENTS G \in TypeEnv = Identifier -> Type {} is the empty TypeEnv x:T,y:U is the mapping {x |-> T, y |-> U} Adding: If G = x:T then G,y:U is x:T,y:U Overlay: G(+)H is such that (G(+)H)(x) = H(x), if x in dom(H) G(x) if x not in dom(H) TYPE CHECKING FOR THE WHILE LANGUAGE ::= int | bool | ok define Programs: x1:int,...,xn:int |- S : ok [prog] -------------------------------- |- proc (x1,...,xn) is { S } : ok if n >= 0 and x1,...,xn are distinct Statements: G |- E : T [asgn] ------------------- if G(x) = T G |- x := E : ok [skip] G |- skip : ok [seq] -------------------------- G |- S1; S2: ok [if] ------------------------- G |- if E then S1 else S2 : ok G |- E : bool, G |- S: ok [wh] -------------------------- while E do S : ok Expressions: [var] G |- x : T if G(x) = T [num] G |- n : int [aexp] ----------------------------- G |- E1 op_a E2 : int [true] G |- true: bool [false] G |- false: bool G |- E : bool [not] --------------------- G |- not E : bool G |- E1 : bool, G |- E2 : bool [bexp] ----------------------------- G |- E1 op_b E2 : bool [rexp] ----------------------------- G |- E1 op_r E2 : bool OPERATOR TYPES G |- E1: S, G |- E2: S, G |- op : S x S -> T [binexp] ------------------- G |- E1 op E2 : T Either have axioms for various operators: G |- + : int x int -> int G |- and : bool x bool -> bool G |- < : int x int -> bool Or put them in the initial environment for the program: G_FV |- S : ok [prog] ----------------- |- { S } : ok if x \in FV(S) ==> G_FV(x) = int and G(+) = int x int -> int, ... TYPE CHECKING FOR SML SUBSET Abstract Syntax: E ::= I | E1(E2) | if E1 then E2 else E3 | fn I:T1 => E | let D in E2 | (E1,E2) D ::= val I:T = E | rec I:T = E | D1; D2 T ::= int | bool | T1 -> T2 | T1 * T2 | TV | T1 list | all TV . T1 TV ::= 'a | 'b | 'c | ... Expressions (E): [var] G |- I: T if [app] ---------------------- G |- E1(E2): T [if] ------------------------------- G |- (if E1 then E2 else E3): T [fn] --------------------------- if G' G |- (fn I:T1 => E): T1->T2 [fnp] --------------------------- if G' G |- (fn (I1,I2): ) => E) : [pair] --------------------- G |- (E1,E2) : G |- D ==> G', G'' |- E2 : [let] --------------------- if G'' = G |- (let D in E2): T Declarations (D): [val] ----------------------- if G' = I:T G |- val I:T = E ==> G' [rec] ----------------------- if G' = I:T G |- rec I:T = E1 ==> G' [Dseq] ---------------------- G |- D1; D2 ==> PROOFS OF TYPE CHECKING [var] Gfxy |- x: 'a, Gfxy |- y :'b [pair] ------------------------------ [var] Gfxy |- f :'a*'b->'c, Gfxy |- (x,y) : 'a*'b [app] ---------------------------- Gfxy |- f(x,y) : 'c [fn] ---------------------------- Gxy |- (fn f:'a*'b->'c => f(x,y)) : ('a*'b->'c) -> 'c [fnp] ---------------------------- ETE |- (fn (x,y):'a*'b => (fn f:'a*'b->'c => f(x,y))) : 'a*'b -> (('a*'b->'c) -> 'c) where ETE = {} and Gxy = x:'a, y:'b and Gfxy = f: 'a*'b->'c, x:'a, y:'b TYPE RECONSTRUCTION EXAMPLE [var] Gfxy |- x: , Gfxy |- y : [pair] ------------------------------ [var] Gfxy |- f : Gfxy |- (x,y) : [app] ---------------------------- Gfxy |- f(x,y) : [fn] ---------------------------- Gxy |- (fn f => f(x,y)) : [fnp] ---------------------------- ETE |- (fn (x,y) => (fn f => f(x,y))) : where ETE = {} and Gxy = x:'a, y:'b} and Gfxy = f: x:'a, y:'b CONSTRAINTS: TYPE RECONSTRUCTION (INFERENCE) PART 1 [var] G |- I: T if G(I) = T G |- E1: S -> T, G |- E2: S [app] --------------------------- G |- E1(E2): T G |- E1:bool, G |- E2:T, G |- E3:T [if] ---------------------------------- G |- (if E1 then E2 else E3): T [fn] ------------------------ if G' = G |- (fn I => E): T1->T2 G |- D ==> G', G'' |- E : T [let] -------------------- if G'' = G(+)G' G |- (let D in E): T WHEN IS A FUNCTION POLYMORPHIC? Is this a type error? (fn f => g(f(2), f(true))) What if f is: val id = (fn x => x) val succ = (fn x => x + 1) DON'T ALLOW CAPTURING let val g = (fn x => let val f = (fn y => x) in if f(3) then f(true) else x + 5 end) in ... end GENERIC TYPE VARIABLES in the type: all 'a . 'a -> 'b 'a is generic, and 'b is not [var] G |- id: all 'a . 'a -> 'a [gElim] -------------------------------- G |- id: 'b -> 'b, G |- id: 'c -> 'c, G |- 3: int, G |- true : bool [app] ------------------------------------ G |- id(3):int, G |- id(true): bool [pair] ----------------------------------- G |- (id(3), id(true)) : int * bool where G = {id |-> (all 'a . 'a -> 'a), 3 |-> int, true |-> bool} CONSTRAINTS: 'b = int 'c = bool TYPE RECONSTRUCTION (INFERENCE) GENERICS and BINDINGS G |- E: T [val] ____________________ G |- (val I = E) ==> G' if G' = I:gen(T,G) G'' |- E1: T [rec] ___________________ G |- rec f = E1 ==> G' if G' = I:gen(T,G) where gen(T, G) = all V1 . ... . all Vk . T if V1,...,Vk are all the free type variables in T that are not in range(G). G |- I : all V . T [gElim] __________________ G |- I : T' if T' = [T''/V]T and T'' is fresh EXAMPLE G |- rec map = (fn f => (fn ls => if (null ls) then [] else (f (hd ls))::(map f (tl ls)))) ==> where G is (op ::): all 'a . 'a * 'a list -> 'a list, [] : all 'a . 'a list, null : all 'a . 'a list -> bool, hd : all 'a . 'a list -> 'a, tl : all 'a . 'a list -> 'a list NO POLYMORPHIC FUNCTIONS AS ARGUMENTS - fun F f = fn (a,b) => (f(a), f(b)); val F = fn : ('a -> 'b) -> 'a * 'a -> 'b * 'b - val W = (fn x => (x x)); std_in:1.18-1.20 Error: operator is not a function operator: 'Z in expression: (x x)