140 likes | 299 Views
Refinement types for ML. By Tim Freeman, Frank Pfenning @ CMU Presented by: Andrey Utis. The Dilemma. Find the last element in a list: fun last (x::nil) = x | last (x::L) = last(L); What is last(nil) ? Solution #1: exceptions non-exhaustive match failure (SML)
E N D
Refinement types for ML By Tim Freeman, Frank Pfenning @ CMU Presented by: Andrey Utis
The Dilemma • Find the last element in a list: fun last (x::nil) = x | last (x::L) = last(L); • What is last(nil) ? • Solution #1: exceptions • non-exhaustive match failure (SML) • Solution #2: refinement types • last: `a non-empty-list -> `a
Refinement types • A is a refinement of B iff every element of type A also has type B • Finding the best refinement type is undecidable in general • function can run M on x • Solution: let the programmer specify the refinement types
Example • Standard list type: datatype `a list = nil | cons of `a * `a list • Non-empty list refinement type: rectype `a nonemptylist = cons of `a * `a list • Singleton list refinement type: rectype `a singleton = cons(`a, nil)
Refinements as lattices • We can think of refinements as lattices list singleton OR nil singleton nil ?
Decidability • Decidable since the lattices have finite depth • This is why we needed refinements to be specified by the programmer
Abstract Interpretation • Perform abstract interpretation using the lattice • Need a “constructor” function – e.g. how do we get a singleton from another type? • This can be derived from the “rectype” definition
Example of cons • Singleton cons(`a * nil) -> `a singleton cons(`a * `a singleton) -> `a list cons(`a * `a list) -> `a list
Functions & polymorphism • How do we deal with functions? • Contravariance a->b refines c->d iff c refines a and b refines d • Polymorphism – use bounded quantification • 8 ra::a ra->ra • almost the same as standard type inference
The algorithm • First infer standard ML types, then refine • Additional inference rules: • contravariance • refinement transitivity
Inference rules • LOOP is used for successive approximations of recursive function types • The new inference rules: • G |- LOOP(f, ly.e, ? , L) • G |-LOOP(f, ly.e, C1, L); G,f:C1::L |-ly.e:C2::L => G |- LOOP(f, ly.e, C2, L) • G |-LOOP(f, ly.e, C, L); G,f:C::L |-ly.e:C::L => G |- fix f. ly.e:C::L
Soundness • Theorem: for all valid type environments G and expressions e, if e evaluates to v and G|- e:D::L then G|- v:D`::L for some D` D • Proof is by induction on the structure of G
Implementation • There is an implementation for Mini-ML • Theory vs Practice datatype maybe = true|false|maybe rectype tt=true and ff=false and tf=true|false let f x = fun true->false | false -> x | maybe->false Theory: f:maybe->maybe Practice: f:maybe->ff
Conclusion • Decidable version of type refinement • Efficiency can be a problem • Type refinements can be thought of as implicit documentation • May be used to make execution faster