Practical Refinement-Type Checking
Total Page:16
File Type:pdf, Size:1020Kb
Practical Refinement-Type Checking Rowan Davies CMU-CS-05-110 May, 2005 School of Computer Science Computer Science Department Carnegie Mellon University Pittsburgh, PA Thesis Committee Frank Pfenning, Chair Robert Harper Peter Lee John Reynolds Alex Aiken, Stanford University Submitted in partial fulfillment of the requirements for the degree of Doctor of Philosophy This research was sponsored in part by the National Science Foundation under grant no. CCR-0204248, and in part by a Hackett Studentship from the University of Western Australia. The views and conclusions contained in this document are those of the author and should not be interpreted as representing the official policies, either expressed or implied, of any sponsoring institution, the U.S. government or any other entity. Keywords: Programming languages, type systems, intersection types, bidirectional checking, value restricted polymorphism, regular-tree types. Abstract Software development is a complex and error prone task. Programming languages with strong static type systems assist programmers by capturing and checking the fundamental structure of programs in a very intuitive way. Given this success, it is natural to ask: can we capture and check more of the structure of programs? In this dissertation I describe an approach called refinement-type checking that allows many common program properties to be captured and checked. This approach builds on the strength of the type system of a language by adding the ability to specify refinements of each type. Following previous work, I focus on refinements that include subtyping and a form of intersection types. Central to my approach is the use of a bidirectional checking algorithm. This does not attempt to infer refinements for some expressions, such as functions, but only checks them against refinements. This avoids some difficulties encountered in previous work, and requires that the programmer annotate their program with some of the intended refinements. The required annotations appear to be very reasonable. Further, they document properties in a way that is natural, precise, easy to read, and reliable. I demonstrate the practicality of my approach by showing that it can be used to design a refinement-type checker for a widely-used language with a strong type system: Standard ML. This requires two main technical developments. Firstly, I present a new variant of intersection types that obtain soundness in the presence of call-by-value effects by incorporating a value restriction. Secondly, I present a practical approach to incorporating recursive refinements of ML datatypes, including a pragmatic method for checking the sequential pattern matching construct of ML. I conclude by reporting the results of experiments with my implementation of refinement- type checking for SML. These indicate that refinement-type checking is a practical method for capturing and checking properties of real code. iii iv Contents 1 Introduction 1 1.1 Thesis . 1 1.2 Motivation . 1 1.3 Background: refinement types . 2 1.4 Our approach . 3 1.4.1 Bidirectional checking . 3 1.4.2 Intersection types with call-by-value effects . 4 1.4.3 Datasorts and pattern matching . 5 1.4.4 Extending to a sort checker for Standard ML . 6 1.5 Introductory examples . 7 1.6 Related work . 9 1.6.1 Previous work on refinement types for ML . 9 1.6.2 Refinement types for LF . 11 1.6.3 Other forms of refinements of types . 11 1.6.4 Program analysis via types . 13 1.6.5 Other forms of more detailed types . 13 1.6.6 Soft typing . 14 1.6.7 Intersection types . 15 1.6.8 Regular tree types . 17 1.7 Organization of this dissertation . 18 2 Sort checking with standard intersection types 19 2.1 Syntax . 20 2.2 Validity judgments . 21 2.3 Reduction . 24 2.4 Declarative subsorting . 24 2.5 Sort equivalence and finiteness . 25 2.6 Relating base refinements and finite lattices . 27 2.7 Algorithmic base subsorting . 29 2.8 Declarative sort assignment . 32 2.9 Algorithmic subsorting . 33 2.10 Bidirectional sort checking . 39 2.10.1 Syntax for annotations . 39 2.10.2 Algorithm . 41 v 2.10.3 Soundness and completeness . 42 2.11 Annotatability . 49 3 Sort checking with a value restriction 53 3.1 Syntax . 56 3.2 Validity judgments . 57 3.3 Values and reduction . 59 3.4 Base sort lattices . 59 3.5 Declarative subsorting . 61 3.6 Finiteness of refinements . 62 3.7 Algorithmic subsorting . 64 3.8 Declarative sort assignment . 71 3.9 Sorts for constants . 77 3.10 Principal sorts and decidability of inference . 78 3.11 Bidirectional sort checking . 83 3.11.1 Syntax . 83 3.11.2 Sort checking algorithm . 84 3.11.3 Soundness and completeness . 85 3.12 Annotatability . 91 4 Soundness with effects 95 4.1 Syntax . 96 4.2 Subtyping . 98 4.3 Typing of terms . 99 4.4 Typing of stores and states . 102 4.5 Reduction semantics . 102 5 Datasort declarations 107 5.1 Syntax . 108 5.2 Comparison with previous signatures . 109 5.3 Validity judgments . 110 5.4 Semantics of datasorts . 112 5.5 Inclusion algorithm . 113 5.6 Correctness of the inclusion algorithm . 115 5.7 Extending inclusion to functions . 122 5.8 An inductive counterexample-semantics with functions . 127 5.8.1 Syntax and semantics . 129 5.8.2 Soundness and Completeness . 130 6 Sort checking with datasorts and pattern matching 139 6.1 Splitting, inversion principles and sorts of constructors . 139 6.2 Syntax with pattern matching . 145 6.3 Typing and pattern contexts . 146 6.4 Reduction semantics . 147 6.5 Declarative sort assignment . 152 6.6 Progress and sort preservation theorem . 159 vi 6.7 Bidirectional sort checking with pattern matching . 181 6.7.1 Syntax . 181 6.7.2 Sort checking algorithm . 181 6.7.3 Termination of sort checking . 186 6.7.4 Soundness of sort checking . 187 6.7.5 Completeness of sort checking . 192 6.7.6 Annotatability . 227 7 Extending to Standard ML 231 7.1 Annotation syntax . 231 7.2 Default sorts . 232 7.3 Layered patterns . 234 7.4 Parameterized datatypes . 236 7.4.1 Adding parameters to datasort declarations . 236 7.4.2 Inclusion of parameterized datasorts using variances . 236 7.4.3 Incompleteness of inclusion via variances . 237 7.4.4 Issues with instantiations of datatypes . 240 7.5 Parametric polymorphism . 241 7.6 Other core SML constructs . 243 7.6.1 Exceptions . 244 7.6.2 Let expressions . 244 7.6.3 Local datatype and datasort declarations . ..