Towards a Practical Programming Language Based on Dependent Type Theory
Total Page:16
File Type:pdf, Size:1020Kb
Thesis for the degree of Doctor of Philosophy Towards a practical programming language based on dependent type theory Ulf Norell Department of Computer Science and Engineering Chalmers University of Technology and Goteborg¨ University G¨oteborg, Sweden, 2007 Towards a practical programming language based on dependent type theory Ulf Norell c Ulf Norell, 2007 ISBN 978-91-7291-996-9 ISSN 0346-718X Doktorsavhandlingar vid Chalmers tekniska h¨ogskola, Ny serie Nr 2677. Technical report 33D Department of Computer Science and Engineering Research group: Programming Logic Department of Computer Science and Engineering Chalmers University of Technology and Goteborg¨ University SE-412 96 G¨oteborg Sweden Telephone +46 (0)31-772 1000 Printed at the Department of Computer Science and Engineering G¨oteborg, 2007 3 Abstract Dependent type theories [ML72] have a long history of being used for theorem proving. One aspect of type theory which makes it very powerful as a proof language is that it mixes deduction with computation. This also makes type theory a good candidate for programming—the strength of the type system allows properties of programs to be stated and established, and the computational properties provide semantics for the programs. This thesis is concerned with bridging the gap between the theoretical presentations of type theory and the requirements on a practical program- ming language. Although there are many challenging research problems left to solve before we have an industrial scale programming language based on type theory, this thesis takes us a good step along the way. In functional programming languages pattern matching provides a concise notation for defining functions. In dependent type theory, pattern matching becomes even more powerful, in that inspecting the value of a particular term can reveal information about the types and values of other terms. In this thesis we give a type checking algorithm for definitions by pattern matching in type theory, supporting overlapping patterns, and pattern matching on intermediate results using the with rule [MM04a]. Traditional presentations of type theory suffers from rather verbose no- tation, cluttering programs and proofs with, for instance, explicit type in- formation. One solution to this problem is to allow terms that can be in- ferred automatically to be omitted. This is usually implemented by inserting metavariables in place of the omitted terms and using unification to solve these metavariables during type checking. We present a type checking algo- rithm for a theory with metavariables and prove its soundness independent of whether the metavariables are solved or not. In any programming language it is important to be able to structure large programs into separate units or modules and limit the interaction between these modules. In this thesis we present a simple, but powerful module sys- tem for a dependently typed language. The main focus of the module system is to manage the name space of a program, and an important characteristic is a clear separation between the module system and the type checker, making it largely independent of the underlying language. As a side track, not directly related to the use of type theory for pro- gramming, we present a connection between type theory and a first-order logic theorem prover. This connection saves the user the burden of prov- ing simple, but tedious first-order theorems by leaving them for the prover. We use a transparent translation to first-order logic which makes the proofs constructed by the theorem prover human readable. The soundness of the 4 connection is established by a general metatheorem. Finally we put our work into practise in the implementation of a pro- gramming language, Agda, based on type theory. As an illustrating example we show how to program a simple certified prover for equations in a commu- tative monoid, which can be used internally in Agda. Much more impressive examples have been done by others, showing that the ideas developed in this thesis are viable in practise. 5 Acknowledgements I would like to thank all the people without whom this thesis would not have been possible. My supervisor Patrik Jansson for supporting me throughout my studies, my colleagues Andreas Abel, Catarina Coquand, Thierry Coquand, Nils Anders Danielsson, Peter Dybjer, and many more for excellent collaborations and many fruitful discussions about my work, Conor McBride for providing invaluable feedback on the thesis, my fianc´eeCecilia for keeping me sane during the process of writing the thesis, and last but not least, my mother for teaching me programming 20 some years ago. 6 Contents 1 Introduction 11 1.1 Overview of the thesis . 12 1.2 Context . 13 1.3 A basic dependent type theory . 14 1.4 Type checking . 18 1.5 Extensions to the theory . 24 1.5.1 Inductive definitions . 24 1.5.2 Uniqueness of identity proofs . 25 1.5.3 Record types . 25 1.5.4 Implicit arguments . 26 2 Pattern Matching 27 2.1 Type checking pattern match equations . 30 2.1.1 Context mappings . 30 2.1.2 Overview of the algorithm . 31 2.1.3 Matching . 31 2.1.4 Unification . 32 2.1.5 Context splitting . 33 2.1.6 Type checking algorithm . 36 2.1.7 Checking inaccessible patterns . 37 2.1.8 Refuting elements of empty types . 38 2.1.9 Checking the right hand side . 39 2.2 Coverage checking . 40 2.2.1 Coverage algorithm . 42 2.2.2 Uniqueness of identity proofs . 44 2.3 The with construct . 45 2.3.1 Examples . 46 3 Metavariables 49 3.1 Introduction . 49 3.2 The underlying logic MLF .................... 51 7 8 CONTENTS 3.3 The type checking algorithm . 53 3.3.1 Operations on the signature . 54 3.3.2 The algorithm . 55 3.4 Examples . 60 3.5 Proof of correctness . 62 3.5.1 Soundness without constraint solving . 62 3.5.2 Soundness of constraint solving . 65 3.5.3 Relating user expressions and checked terms . 68 3.5.4 Main result . 68 3.6 Implicit arguments . 69 3.7 Extending the underlying theory . 70 3.7.1 Sigma types and the unit type . 71 3.7.2 Function types as terms . 72 3.7.3 Universe hierarchy . 72 3.7.4 Pattern matching . 72 3.8 Summary . 73 4 Module System 75 4.1 Introduction . 75 4.2 Description . 76 4.2.1 Private definitions . 77 4.2.2 Name modifiers . 78 4.2.3 Re-exporting names . 79 4.2.4 Parameterised modules . 79 4.2.5 Splitting a program over multiple files . 81 4.3 Equipment for record types . 82 4.4 An example . 83 4.4.1 A note on record subtyping . 87 4.5 Implementation . 87 4.5.1 Scope checking state . 88 4.5.2 Looking up and adding names . 89 4.5.3 Pushing and popping . 89 4.5.4 Scope modifiers . 90 4.5.5 Scope checking . 91 4.5.6 Type checking . 93 4.6 Summary . 95 5 The Agda Language 97 5.1 Language description . 97 5.1.1 Names . 97 5.1.2 Interaction points . 98 CONTENTS 9 5.1.3 Implicit syntax . 98 5.1.4 Functions . 98 5.1.5 Implicit arguments . 99 5.1.6 Datatypes and function definitions . 100 5.1.7 Records . 102 5.1.8 Local definitions . 102 5.1.9 Module system . 103 5.1.10 Additional features . 104 5.2 A bigger example . 104 5.2.1 Logic . 105 5.2.2 Basic datatypes . 105 5.2.3 Equivalence relations . 108 5.2.4 Chain reasoning . 112 5.2.5 Monoids . 114 5.2.6 Representing commutative monoid equations . 115 5.2.7 Semantics . 118 6 First-order Logic 125 6.1 Introduction . 125 6.2 The Logical Framework MLFProp ................. 127 6.3 Translation from MLFProp toFOL ................ 132 6.3.1 Formal Description of the Translation . 132 6.3.2 Resolution Calculus . 134 6.3.3 Proof of Correctness . 135 6.3.4 Simple Examples . 138 6.4 Implementation . 139 6.4.1 Implicit Arguments . 140 6.4.2 The Plug-in Mechanism . 141 6.4.3 The FOL Plug-in . 141 6.5 Examples . 142 6.5.1 Relational Algebra . 143 6.5.2 Category Theory . 144 6.5.3 Computer Algebra . 145 6.6 Related Work . 148 6.7 Future Work . 150 7 Conclusions 153 10 CONTENTS Chapter 1 Introduction Programming is the craft of giving instructions to machines. Being machines they will follow these instructions regardless of whether they make sense or not. The purpose of a programming language is to make it easier to express the things that do make sense while making it harder or impossible to express things that do not make sense. The first step in this direction is to make programming languages readable by human beings. That way the programmer can read her program and convince herself that it makes sense, but since programmers are humans they will make mistakes both writing programs and trying to make sense of them. To help with this modern programming languages come equipped with type systems, which allows the programmer to declare the purpose of a program in the form of a type. The machine can then check that a given program has the intended behaviour. Types can also be used to guide the programmer in constructing the correct program. In its simplest form a type system allows you to state, for instance, that the purpose of a sorting program is to take a list as input and produce a list as output. This is a very crude approximation of what it means to be a sorting program but it does provide some guarantees. In more expressive type systems, such as the ones discussed in this thesis, it is possible to express that the sorting program takes a list of elements over which there is a total order, and computes an ordered permutation of this list.