Programs as data Higher-order functions, polymorphic types, and type inference Peter Sestoft Monday 2013-09-16 www.itu.dk 1 Plan for today • Higher-order functions in F# • A higher-order functional language • F# mutable references • Polymorphic types – Informal procedure – Type rules – Unification – The union-find data structure – Type inference algorithm • Variant generic types in Java and C# – Java use-side variance – C# 4.0 declaration-side variance www.itu.dk 2 Higher-order functions and anonymous functions in F# • A higher-order function takes another function as argument (’a->’b) -> (’a list -> ’b list) let rec map f xs = match xs with | [] -> [] | x::xr -> f x :: map f xr let mul2 x = 2.0 * x;; map mul2 [4.0; 5.0; 89.0];; [8.0; 10.0; 178.0] • Anonymous functions map (fun x -> 2.0 * x) [4.0; 5.0; 89.0] [false; false; true] map (fun x -> x > 10.0) [4.0; 5.0; 89.0] www.itu.dk 3 Function types in C# unit -> R • Delegate types A1 -> R delegate R Func<R>() delegate R Func<A1,R>(A1 x1) delegate R Func<A1,A2,R>(A1 x1, A2 x2) A1 * A2 -> R delegate void Action<A1>(A1 x1) delegate void Action<A1,A2>(A1 x1, A2 x2) • Anonymous method expressions delegate(int x) { return x>10; } delegate(int x) { return x*x; } (int x) => x>10 x => x>10 x => x*x C# 3.0 A1 -> unit A1*A2 -> unit Func<int,bool> Func<int,int> fun (x:int) -> x>10 fun x -> x>10 fun x -> x*x F# 4 Uniform iteration over a list let rec sum xs = match xs with | [] -> 0 | x::xr -> x + sum xr int list -> int let rec prod xs = match xs with | [] -> 1 | x::xr -> x * prod xr • Generalizing 0/1 to e, and +/* to f: let rec foldr f xs e = match xs with | [] -> e | x::xr -> f x (foldr f xr e) ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b List.foldBack in F# The foldr function replaces :: by f, and [] by e: foldr ◊ (x1::x2::…::xn::[]) e = x1 ◊ (x2 ◊ (... ◊ (xn ◊ e) …)) F# mutable references • A reference is a cell that can be updated Create int reference let r = ref 177 !r Dereference (r := !r+1; !r) !r Assign to reference • Useful for generation of new names etc: let nextlab = ref -1;; let newLabel () = (nextlab := 1 + !nextlab; "L" + string (!nextlab));; newLabel();; newLabel();; newLabel();; www.itu.dk Higher-order micro-ML/micro-F# • Higher-order functional language – A function may be given as argument: let twice g x = g(g x) – A function may be returned as result let add x = let f y = x+y in f let addtwo = add 2 let x = 77 addtwo 5 add has two arguments! • Closures are needed: – The function returned must enclose the value of f’s parameter x – has nothing to do with later x • Same micro-ML syntax: Fun/Absyn.fs www.itu.dk 7 Interpretation of a higher-order language • The closure machinery is already in place • Just redefine function application: let rec eval (e : expr) (env : value env) : value = match e with | ... | Call(eFun, eArg) -> let fClosure = eval eFun env in match fClosure with | Closure (f, x, fBody, fDeclEnv) -> let xVal = eval eArg env let fBodyEnv = (x, xVal) :: (f, fClosure) :: fDeclEnv in eval fBody fBodyEnv | _ -> failwith "eval Call: not a function" www.itu.dk 8 ML/F#-style parametric polymorphism let f x = 1 in f 2 + f true int -> int Type for f is ’a -> int bool -> int • Each expression has a compile-time type • The type may be polymorphic (‘many forms’) and have multiple type instances www.itu.dk 9 Type generalization and specialization • If f has type (α → int) and α appears nowhere else, the type gets generalized to a type scheme written ∀α.(α → int): let f x = 1 ∀α.(α → int) • If f has type scheme ∀α.(α → int) then α may be instantiated by/specialized to any type: f f f f 42 false [22] (3,4) f : int → int f : bool → int f : int list → int f : int*int → int www.itu.dk 10 Polymorphic type inference • F# and ML have polymorphic type inference • Static types, but not explicit types on functions α = β→δ α β let twice g y = g (g y) (β→β) → (β→β) β=δ β=δ=ε and δ=ε so so αα==β→β β→β α = δ→ε • We generalize β, so twice gets the type scheme ∀β. (β→β) → (β→β), hence “β may be any type” let mul2 y = 2 * y twice mul2 11 mul: int -> int twice : (int->int)->(int->int) Basic elements of type inference • “Guess” types using type variables α, β, … • Build and solve “type equations” α = β→δ … • Generalize types of let-bound variables/funs. to obtain type schemes ∀β. (β→β) → (β→β) • Specialize type schemes at variable use • This type system has several names: – ML-polymorphism – let-polymorphism – Hindley-Milner polymorphism (Hindley 1969 & Milner 1978) www.itu.dk 12 Restrictions on ML polymorphism, 1 • Only let-bound variables and functions can have a polymorphic type • A parameter’s type is never polymorphic: let f g = g 7 + g false Ill-typed: parameter g never polymorphic • A function is not polymorphic in its own body: let rec h x = if true then 22 else h 7 + h false Ill-typed: h not polymorphic in its own body www.itu.dk 13 Restrictions on ML polymorphism, 2 • Types must be finite and non-circular f not polymorphic in its own body let rec f x = f f • Guess x has type α • Then f must have type α→β for some β • But because we apply f to itself in (f f), we must have α = α→β • But then α = (α→β)→β = ((α→β) →β)→β = … is not a finite type • So the example is ill-typed www.itu.dk 14 Restrictions on ML polymorphism, 3 • A type parameter that is used in an enclosing scope cannot be generalized g : β→int α α= β let f x = β let g y = if x=y then 11 else 22 in g false in f 42 α bound in outer scope, cannot Ill-typed: function g generalize β not polymorphic • Reason: If this were well-typed, we would compare x (42) with y (false), not good… www.itu.dk 15 Joint exercises • Which of these are well-typed, and why/not? let f x = 1 in f f let f g = g g let f x = let g y = y in g false in f 42 let f x = let g y = if true then y else x in g false in f 42 Type rules for ML-polymorphism Specialize from typescheme Generalize to typescheme Joint exercises • Draw the type trees for some of these let x = 1 in x < 2 let f x = 1 in f 2 + f false let f x = 1 in f f www.itu.dk 18 Programming type inference • Algorithm W (Damas & Milner 1982) with many later improvements • Symbolic type equation solving by – Unification – The union-find data structure • “Not free in ρ” formalized by binding levels: 0 α:0 α= β let f x = :1 β:0 1 let g y = if x=y then 11 else 22 in g false in f 42 • Since β-level < g-level, do not generalize β www.itu.dk Unification of two types, unify(t1,t2) Type t1 Type t2 Action int int No action bool bool No action t1x → t1r t2x → t2r unify(t1x,t2x) and unify(t1r,t2r) α α No action α β Make α=β α t2 Make α=t2 unless t2 contains α t1 β Make β=t1 unless t1 contains β All other cases Failure, type error! www.itu.dk The union-find data structure • A graph of nodes (type variables) divided into disjoint classes • Each class has a representative node • Operations: – New: create new node (type variable) – Find(n): find representative of node n’s class – Union(n1,n2): join the classes of n1 and n2 www.itu.dk 21 Type inference for micro-ML, 1 let rec typ (lvl : int) (env : tenv) (e : expr) : typ = match e with | CstI i -> TypI | CstB b -> TypB | Var x -> specialize lvl (lookup env x) | ... typ ρ e = t if and only if www.itu.dk 22 Type inference for micro-ML, 2 let rec typ (lvl : int) (env : tenv) (e : expr) : typ = match e with | Prim(ope, e1, e2) -> let t1 = typ lvl env e1 let t2 = typ lvl env e2 match ope with | "*" -> (unify TypI t1; unify TypI t2; TypI) | "+" -> (unify TypI t1; unify TypI t2; TypI) | "=" -> (unify t1 t2; TypB) | "<" -> (unify TypI t1; unify TypI t2; TypB) | "&" -> (unify TypB t1; unify TypB t2; TypB) | _ -> failwith ("unknown primitive " ^ ope) Type inference for micro-ML, 3 let rec typ (lvl : int) (env : tenv) (e : expr) : typ = match e with | If(e1, e2, e3) -> let t2 = typ lvl env e2 let t3 = typ lvl env e3 unify TypB (typ lvl env e1); unify t2 t3; t2 www.itu.dk 24 Type inference for micro-ML, 4 let rec typ (lvl : int) (env : tenv) (e : expr) : typ = match e with | ... | Let(x, eRhs, letBody) -> let lvl1 = lvl + 1 let resTy = typ lvl1 env eRhs let letEnv = (x, generalize lvl resTy) :: env typ lvl letEnv letBody | ... www.itu.dk 25 Properties of ML-style polymorphism • The type found by the inference algorithm is the most general one: the principal type • Consequence: Type checking can be modular • Types can be large and type inference slow: let let let let let let let id x pair p1 p p2 p p3 p p4 p p5 p = x = = = = = x y p = p pair id pair p1 pair p2 pair p3 pair p4 x y id p p1 p p2 p p3 p;; p4 p;; Exponentially many type variables! • In practice types are small and inference fast www.itu.dk Type inference in C# 3.0 var x = “hello”; … x.Length … x = 17; // Inferred type: String // Type error • No polymorphic generalization • Can infer parameter type of anonymous function from context: xs.Where(x=>x*x>5) • Cannot infer type of anonymous function • Parameter types in methods – must be declared – cannot be inferred, because C# allows method overloading … www.itu.dk 27 Polymorphism (generics) in Java and C# • Polymorphic types interface IEnumerable<T> { ... } class List<T> : IEnumerable<T> { ... } struct Pair<T,U> { T fst; U snd; ... } delegate R Func<A,R>(A x); • Polymorphic methods void Process<T>(Action<T> act, T[] xs) void <T> Process(Action<T> act, T[] arr) C# Java • Type parameter constraints void Sort<T>(T[] arr) where T : IComparable<T> void <T extends Comparable<T>> Sort(T[] arr) www.itu.dk C# Java 28 Variance in type parameters • Assume Student subtype of Person void PrintPeople(IEnumerable<Person> ps) { ... } IEnumerable<Student> students = ...; Java and C# 3 say PrintPeople(students); NO: Ill-typed! • C# 3 and Java: – A generic type is invariant in its parameter – I<Student> is not subtype of I<Person> • Co-variance (co=with): – I<Student> is subtype of I<Person> • Contra-variance (contra=against): – I<Person> is subtype of I<Student> www.itu.dk 29 Co-/contra-variance is unsafe in general • Co-variance is unsafe in general List<Student> ss = new List<Student>(); Wrong! List<Person> ps = ss; Because would allow ps.Add(new Person(...)); writing Person to Student s0 = ss[0]; Student list • Contra-variance is unsafe in general List<Person> ps = ...; List<Student> ss = ps; Student s0 = ss[0]; • But: Wrong! Because would allow reading Student from Person list – co-variance OK if we only read (output) from list – contra-variance OK if we only write (input) to list www.itu.dk 30 Java 5 wildcards • Use-side co-variance void PrintPeople(ArrayList<? extends Person> ps) { for (Person p : ps) { … } } ... OK! PrintPeople(new ArrayList<Student>()); • Use-side contra-variance void AddStudentToList(ArrayList<? super Student> ss) { ss.add(new Student()); } ... OK! AddStudentToList(new ArrayList<Person>()); www.itu.dk 31 Co-variance in interfaces (C# 4) • When an I<T> only produces/outputs T’s, it is safe to use an I<Student> where a I<Person> is expected • This is co-variance • Co-variance is declared with the out modifier interface IEnumerable<out T> { IEnumerator<T> GetEnumerator(); } interface IEnumerator<out T> { T Current { get; } } • Type T can be used only in output position; e.g. not as method argument (input) www.itu.dk 32 Contra-variance in interfaces (C# 4) • When an I<T> only consumes/inputs T’s, it is safe to use an I<Person> where an I<Student> is expected • This is contra-variance • Contra-variance is declared with in modifier interface IComparer<in T> { int Compare(T x, T y); } • Type T can be used only in input position; e.g. not as method return type (output) www.itu.dk 33 Variance in function types (C# 4) • A C# delegate type is – co-variant in return type (output) – contra-variant in parameters types (input) • Return type co-variance: Func<int,Student> nthStudent = ... Func<int,Person> nthPerson = nthStudent; • Argument type contra-variance: Func<Person,int> personAge = ... Func<Student,int> studentAge = personAge; • F# does not support co-variance or contravariance (yet?) www.itu.dk 34 Reading and homework • This week’s lecture: – PLC sections A.11-A.12 and 5.1-5.5 and 6.1-6.7 – Exercises 6.1, 6.2, 6.3, 6.4, 6.5 • No lecture next week • Next lecture, Monday 30 September: – PLCSD chapter 7 – Strachey: Fundamental Concepts in … – Kernighan & Richie: The C programming language, chapter 5.1-5.5 www.itu.dk 35

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertising