Two comments on let polymorphism

331 Views

Download Presentation
## Two comments on let polymorphism

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -

**Two comments on let polymorphism**I. What is the (time, space) complexity of type reconstruction? In practice – executes “fast” (seems linear time) But, some bad cases exist last**consider:**let f1 = fun x (x,x);; let f2 = fun y f1(f1 y);; let f3 = fun y f2(f2 y);; ….. …. fn …. fn(some expression that uses fn) How do the types of these functions look like? last**let f1 = fun x (x,x);;**‘a ‘a * ‘a (1 2) let f2 = fun y f1(f1 y);; ‘a (‘a * ‘a) * (‘a * ‘a) (1 4) let f3 = fun y f2(f2 y);; ‘a ( [(‘a * ‘a)*(‘a * ‘a)]*[(‘a * ‘a)*(‘a * ‘a)] )* ( [(‘a * ‘a)*(‘a * ‘a)]*[(‘a * ‘a)*(‘a * ‘a)] ) (1 16) let fn = … (double exponential) last**One can save a lot of space by representing types as**graphs, instead of trees (common sub expression elimination) Double exponential exponential last**Theorem:**Type reconstruction for core ML is exptime-complete This means that worst-case complexity is bad, but in practice it is sufficiently efficient Note: extending type reconstruction to the full calculus with universal types is impossible --- type reconstruction for this calculus is undecidable last**II. polymorphic references are problematic:**let c = ref (lambda x.x);; here, the type for c is c:= lambda x. x+5;; type-checker allows the assignment, type is unit (!c) true;; type checker accepts but, at run-time we apply a function of type intint to true – a run-time error last**One possible solution: lazy evaluation of let :**let x = e // create binding xe …. … x // substitute e for x, and continue evaluation In the example: let c = ref (lambda x. x) // bind c to the expression c:= lambda x. x+5 // substitute binding for x (ref lambda x.x) := lambda x.x+5 // one cell created) (!c) true // substitute binding for x (!(ref lambda x. x)) true // another cell created last**But**• Nobody really knows how to specify or implement lazy evaluation for languages with imperative features (side-effects) – how to order the side-effects? • The examples shows this leads to a semantics that is not very useful last**The ML solution:**In let x = e in … Allow to generalize the type for x only if e is a syntactic value is is not is not Statistics collected on systems w/o this restriction (a more liberal but complex solution) there are almost no programs where this restriction hurts. last**Object-oriented languages – some concepts**A well known feature of OO pl’s is sub-type polymorphism We concentrate on this subject last**What is sub-type ?**Two possible answers: A type t is (denotes) a set of values With the second, int<: float holds; Compiler inserts the coercion during type-checking (so a bit more complexity of type-checking is expected) We use the first (simpler intuition) last**The basic intuition of sub-typing:**List to the type-checker level : an expression of a sub-type can be safely used in a context where an expression of a type is expected use is defined by the operations available on the two types sub-typing is not a new independent feature, it interacts with the other components of a type system last**Sub-typing with records and cells**Objects are similar to records convenient to introduce sub-typing in the context of a language with : base types, records, functions, ref cells We assume some (possibly none) sub-type axioms are given for the base types last**From the basic intuition :**The interaction of sub-typing with the type-checker : the subsumption rule: Wherever the type-checker expects a type, it allows a sub-type Note: algorithmically, this rule is problematic last**Rules independent of the given type system:**From the basic intuition, sub-typing is reflexive and transitive Second rule also looks a bit problematic (algorithmically) last**Rules for records :**Example : let f = lambda x : {a:int} . x.a;; seems reasonable to apply f also to {a=4, b=“john”}, since f uses only x.a But, also make sense to allow a sub-type in a field last**Using these two rules, we can prove:**Using reflexivity, we can refine a single field, rather than all last**By combining the two record rules with transitivity, we**can change both the number of fields and their types last**Can combine to one comprehensive record rule:**Note: we assume that in a record, order of fields is irrelevant, so in all the rules, the label-type pairs are assumed to be a set. This can be emphasized by a rule that allows to change position of fields Can “add” fields anywhere in a record last**Rules for functions :**These can be applied, be passed as arguments/return values If a context requires a function that for an argument of type return a value of type , then a function that returns a value of a sub-type is ok last**But, for the input type:**In a context that expects a function of this type, you also accept a function that has this guarantee for a larger set last**The two rules are typically combined :**Contra-variance for the input type is difficult to swallow; convince yourself that There are also applications where a restriction of the in-type in a co-variant fashion seems desirable Some languages (e.g., Eiffel) also co-variant change on in-type, and leave a hole in the type system last