180 likes | 325 Views
Polymorphic Type System. Goals. Allow expression of “ for all types T ” fun I x = x I : ’a -> ’a Allow expression of type-equality constraints fun fst (x,y) = x fst : ’a *’b -> ’a Support notion of instance of a type I 5 I : int -> int. Type constants
E N D
Polymorphic Type System L4Poly
Goals • Allow expression of “for all types T” fun I x = x I : ’a -> ’a • Allow expression of type-equality constraints fun fst (x,y) = x fst : ’a *’b -> ’a • Support notion of instance of a type I 5 I : int -> int L4Poly
Type constants int, bool, … Type variables ’a, …, ’’a, … Type constructors ->, *, … Principal type of an expression is the most general type, which ML system infers. Type Checking Rule An expression of a type can legally appear in all contexts where an instance of that type can appear. Polymorphic Type System L4Poly
Signature of Equality = : ’a * ’a -> bool ? • Equality (“= ”) is not computable for all types. E.g., function values. • So types (’a,’b, …) are partitioned into • types that support equality (’’a, ’’b, …) • types that do not support equality. = : ’’a * ’’a -> bool • int, string, etc are equality types. They are closed under cartesian product, but not under function space constructor. L4Poly
Type Inference I (3,5) = (3,5) (int*int)->(int*int) (int*int) Principal type of I is the generalization of all possible types of its uses. Curry2 : (’a * ’b -> ’c) -> (’a -> ’b -> ’c) Uncurry2 : (’a -> ’b -> ’c) -> (’a * ’b -> ’c) L4Poly
Subtle Points • The type of a use of a function must be an instance of the principal type inferred in the definition. • The type of a value is fixed. So, multiple occurrences of a symbol denoting the same value (such as several uses of a formal parameter in a function body) must have identical type. L4Poly
Systematic Type Derivation fun c f g x = f(g(x)) • Step1:Assign most general types to left-hand-side arguments and the (rhs) result. f : t1 g : t2 x : t3 f(g(x)) : t4 Thus, type of c is: c: t1 -> t2 -> t3 -> t4 L4Poly
Step 2: Analyze and propagate type constraints • Application Rule If f x : t then x : t1 and f : t1 -> t, for some new t1. • Equality Rule If both x:t and x:t1 can be deduced for the value of a variable x, then t = t1. • Function Rule (t->u)=(t1->u1)iff(t=t1) /\ (u=u1) L4Poly
f(g x): t4 AR (g x) : t5 f : t5 -> t4 AR x : t6 g: t6 -> t5 ER t1 = t5 -> t4 t2 = t6 -> t5 t3 = t6 Step 3: Overall deduced type c : (t5 -> t4) -> (t6 -> t5) -> (t6 -> t4) (unary function composition) L4Poly
Example fun f x y = fst x + fst y given op + : int ->int -> int fst : ’a * ’b -> ’a Step 1: x: t1 y : t2 (fst_1 x + fst_2 y) : t3 The two instantiations of fst need not have the same type. fst_1 : u1 * u2 -> u1 fst_2 : v1 * v2 -> v1 f: t1 -> t2 -> t3 L4Poly
Step 2: Applying rule for + (fst_1 x) : int (fst_2 y) : int t3 = int • Step 3: fst_1 : int*u2 -> int fst_2 : int*v2 -> int t1 = int * u2 t2 = int * v2 f: int * ’a -> int * ’b -> int L4Poly
Example (fixed point) fun fix f = f (fix f) 1. Assume f : ’a -> ’b 2. From (fix f = f (…)) infer fix : (’a -> ’b) -> ’b 3. From ( ... = f (fix f)) infer fix : (’a -> ’b) -> ’a fix : ’a -> ’a -> ’a L4Poly
Recursive Definition (curried function) fun f x y = f (f x) 0; f: (int -> ’a) -> int -> ’a fun f x y = f (f x) (f x y); f: (’a -> ’a) -> ’a -> ’a fun f f = f; (* identity function *) (* names of formals and functions come from disjoint namespaces *) fun f x y = f x; (* illegal *) fun f g = g f; (* illegal *) L4Poly
Example (ill-typed definition) fun selfApply f = f f 1. f_1:t1 (f_2 f_3):t2 selfApply : t1 -> t2 2. f_3 : t3 f_2 : t3 -> t2 3. t1 = t3 = t3 -> t2 (unsatisfiable) (cf. val selfApply = I I) L4Poly
Problematic Case fn x => (1, “a”); val it = fn : 'a -> int * string fn f => (f 1, f “a”); Type error “Least upper bound” of int and string does not exist, and the expression cannot be typed as ((’a->’b) ->’b*’b) because the following expression is not type correct. ( (fn f => (f 1, f “a”)) (Real.Math.sqrt) ); L4Poly
(cont’d) fn (x,y) => (size x, length y); val it = fn : string * 'a list -> int * int fn z => (size z, length z); Type Error string and list cannot be unified to obtain ’a -> (int * int). Or else, it will conflict with type instantiation rule, compromising type safety. L4Poly
Equivalence? (let val V = E in F end) =/= ((fn V => F) E) Even though both these expressions have the same behavior, the lambda abstraction is more restricted because it is type checked independently of the context of its use. In particular, the polymorphic type variable introduced in an abstraction must be treated differently from that introduced in a local definition. L4Poly