Introduction to Functional Programming

John Harrison

              jrh@cl.cam.ac.uk

3rd December 1997

i

Preface

These are the lecture notes accompanying the course Introduction to Functional Programming, which I taught at Cambridge University in the academic year 1996/7.

This course has mainly been taught in previous years by Mike Gordon. I have retained the basic structure of his course, with a blend of theory and practice, and have borrowed heavily in what follows from his own lecture notes, available in book form as Part II of (Gordon 1988). I have also been influenced by those who have taught related courses here, such as Andy Gordon and Larry Paulson and, in the chapter on types, by Andy Pitts’s course on the subject.

The large chapter on examples is not directly examinable, though studying it should improve the reader’s grasp of the early parts and give a better idea about how ML is actually used.

Most chapters include some exercises, either invented specially for this course or taken from various sources. They are normally intended to require a little thought, rather than just being routine drill. Those I consider fairly difficult are marked with a (*).

These notes have not yet been tested extensively and no doubt contain various errors and obscurities. I would be grateful for constructive criticism from any readers.

   John Harrison (jrh@cl.cam.ac.uk).

Plan of the lectures

This chapter indicates roughly how the material is to be distributed over a course of twelve lectures, each of slightly less than one hour.

1. Introduction and Overview Functional and imperative programming: contrast, pros and cons. General structure of the course: how lambda calculus turns out to be a general programming language. Lambda notation: how it clarifies variable binding and provides a general analysis of mathematical notation. Currying. Russell’s paradox.

2. Lambda calculus as a formal system Free and bound variables. Substitution. Conversion rules. Lambda equality. Extensionality. Reduction and reduction strategies. The Church-Rosser theorem: statement and consequences. Combinators.

3. Lambda calculus as a programming language Computability background; Turing completeness (no proof). Representing data and basic operations: truth values, pairs and tuples, natural numbers. The predecessor operation. Writing recursive functions: fixed point combinators. Let expressions. Lambda calculus as a declarative language.

4. Types Why types? Answers from programming and logic. Simply typed lambda calculus. Church and Curry typing. Let polymorphism. Most general types and Milner’s algorithm. Strong normalization (no proof), and its negative consequences for Turing completeness. Adding a recursion operator.

5. ML ML as typed lambda calculus with eager evaluation. Details of evaluation strategy. The conditional. The ML family. Practicalities of interacting with ML. Writing functions. Bindings and declarations. Recursive and polymorphic functions. Comparison of functions.

6. Details of ML More about interaction with ML. Loading from files. Comments. Basic data types: unit, booleans, numbers and strings. Built-in operations. Concrete syntax and infixes. More examples. Recursive types and pattern matching. Examples: lists and recursive functions on lists.

7. Proving programs correct The correctness problem. Testing and verification. The limits of verification. Functional programs as mathematical objects. Examples of program proofs: exponential, GCD, append and reverse.

8. Effective ML Using standard combinators. List iteration and other useful combinators; examples. Tail recursion and accumulators; why tail recursion is more efficient. Forcing evaluation. Minimizing consing. More efficient reversal. Use of ‘as’. Imperative features: exceptions, references, arrays and sequencing. Imperative features and types; the value restriction.

9. ML examples I: symbolic differentiation Symbolic computation. Data representation. Operator precedence. Association lists. Prettyprinting expressions. Installing the printer. Differentiation. Simplification. The problem of the ‘right’ simplification.

10. ML examples II: recursive descent parsing Grammars and the parsing problem. Fixing ambiguity. Recursive descent. Parsers in ML. Parser combinators; examples. Lexical analysis using the same techniques. A parser for terms. Automating precedence parsing. Avoiding backtracking. Comparison with other techniques.

11. ML examples III: exact real arithmetic Real numbers and finite representations. Real numbers as programs or functions. Our representation of reals. Arbitrary precision integers. Injecting integers into the reals. Negation and absolute value. Addition; the importance of rounding division. Multiplication and division by integers. General multiplication. Inverse and division. Ordering and equality. Testing. Avoiding reevaluation through memo functions.

12. ML examples IV: Prolog and theorem proving Prolog terms. Casesensitive lexing. Parsing and printing, including list syntax. Unification. Backtracking search. Prolog examples. Prolog-style theorem proving. Manipulating formulas; negation normal form. Basic prover; the use of continuations. Examples: Pelletier problems and whodunit.

Contents

1 Introduction 1 1.1 The merits of functional programming . . . . . . . . . . . . . . . 3 1.2 Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2 Lambda calculus 7 2.1 The benefits of lambda notation . . . . . . . . . . . . . . . . . . . 8 2.2 Russell’s paradox . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.3 Lambda calculus as a formal system . . . . . . . . . . . . . . . . . 12

2.3.1 Lambda terms . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.3.2 Free and bound variables . . . . . . . . . . . . . . . . . . . 13 2.3.3 Substitution . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.3.4 Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.3.5 Lambda equality . . . . . . . . . . . . . . . . . . . . . . . 16 2.3.6 Extensionality . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.3.7 Lambda reduction . . . . . . . . . . . . . . . . . . . . . . 18 2.3.8 Reduction strategies . . . . . . . . . . . . . . . . . . . . . 19 2.3.9 The Church-Rosser theorem . . . . . . . . . . . . . . . . . 19

2.4 Combinators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3 Lambda calculus as a programming language 24

3.1 Representing data in lambda calculus . . . . . . . . . . . . . . . . 26 3.1.1 Truth values and the conditional . . . . . . . . . . . . . . 26 3.1.2 Pairs and tuples . . . . . . . . . . . . . . . . . . . . . . . . 27 3.1.3 The natural numbers . . . . . . . . . . . . . . . . . . . . . 29

3.2 Recursive functions . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.3 Let expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.4 Steps towards a real programming language . . . . . . . . . . . . 35 3.5 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4 Types 38

4.1 Typed lambda calculus . . . . . . . . . . . . . . . . . . . . . . . . 39 4.1.1 The stock of types . . . . . . . . . . . . . . . . . . . . . . 40 4.1.2 Church and Curry typing . . . . . . . . . . . . . . . . . . 41 4.1.3 Formal typability rules . . . . . . . . . . . . . . . . . . . . 42 4.1.4 Type preservation . . . . . . . . . . . . . . . . . . . . . . . 43

4.2 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.2.1 Let polymorphism . . . . . . . . . . . . . . . . . . . . . . 45 4.2.2 Most general types . . . . . . . . . . . . . . . . . . . . . . 46

4.3 Strong normalization . . . . . . . . . . . . . . . . . . . . . . . . . 47

5 A taste of ML 50 5.1 Eager evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.2 Consequences of eager evaluation . . . . . . . . . . . . . . . . . . 53 5.3 The ML family . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 5.4 Starting up ML . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 5.5 Interacting with ML . . . . . . . . . . . . . . . . . . . . . . . . . 55 5.6 Bindings and declarations . . . . . . . . . . . . . . . . . . . . . . 56 5.7 Polymorphic functions . . . . . . . . . . . . . . . . . . . . . . . . 58 5.8 Equality of functions . . . . . . . . . . . . . . . . . . . . . . . . . 60

6 Further ML 63 6.1 Basic datatypes and operations . . . . . . . . . . . . . . . . . . . 64 6.2 Syntax of ML phrases . . . . . . . . . . . . . . . . . . . . . . . . 66 6.3 Further examples . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 6.4 Type definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

6.4.1 Pattern matching . . . . . . . . . . . . . . . . . . . . . . . 71 6.4.2 Recursive types . . . . . . . . . . . . . . . . . . . . . . . . 73 6.4.3 Tree structures . . . . . . . . . . . . . . . . . . . . . . . . 76 6.4.4 The subtlety of recursive types . . . . . . . . . . . . . . . 78

7 Proving programs correct 81 7.1 Functional programs as mathematical objects . . . . . . . . . . . 83 7.2 Exponentiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 7.3 Greatest common divisor . . . . . . . . . . . . . . . . . . . . . . . 85 7.4 Appending . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 7.5 Reversing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

8 Effective ML 94 8.1 Useful combinators . . . . . . . . . . . . . . . . . . . . . . . . . . 94 8.2 Writing efficient code . . . . . . . . . . . . . . . . . . . . . . . . . 96

8.2.1 Tail recursion and accumulators . . . . . . . . . . . . . . . 96 8.2.2 Minimizing consing . . . . . . . . . . . . . . . . . . . . . . 98 8.2.3 Forcing evaluation . . . . . . . . . . . . . . . . . . . . . . 101

8.3 Imperative features . . . . . . . . . . . . . . . . . . . . . . . . . . 102 8.3.1 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 8.3.2 References and arrays . . . . . . . . . . . . . . . . . . . . . 104 8.3.3 Sequencing . . . . . . . . . . . . . . . . . . . . . . . . . . 105 8.3.4 Interaction with the type system . . . . . . . . . . . . . . 106

9 Examples 109

9.1 Symbolic differentiation . . . . . . . . . . . . . . . . . . . . . . . 109 9.1.1 First order terms . . . . . . . . . . . . . . . . . . . . . . . 110 9.1.2 Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 9.1.3 Derivatives . . . . . . . . . . . . . . . . . . . . . . . . . . 114 9.1.4 Simplification . . . . . . . . . . . . . . . . . . . . . . . . . 115

9.2 Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 9.2.1 Recursive descent parsing . . . . . . . . . . . . . . . . . . 120 9.2.2 Parser combinators . . . . . . . . . . . . . . . . . . . . . . 120 9.2.3 Lexical analysis . . . . . . . . . . . . . . . . . . . . . . . . 122 9.2.4 Parsing terms . . . . . . . . . . . . . . . . . . . . . . . . . 123 9.2.5 Automatic precedence parsing . . . . . . . . . . . . . . . . 124 9.2.6 Defects of our approach . . . . . . . . . . . . . . . . . . . 126

9.3 Exact real arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . 128 9.3.1 Representation of real numbers . . . . . . . . . . . . . . . 129 9.3.2 Arbitrary-precision integers . . . . . . . . . . . . . . . . . 129 9.3.3 Basic operations . . . . . . . . . . . . . . . . . . . . . . . 131 9.3.4 General multiplication . . . . . . . . . . . . . . . . . . . . 135 9.3.5 Multiplicative inverse . . . . . . . . . . . . . . . . . . . . . 136 9.3.6 Ordering relations . . . . . . . . . . . . . . . . . . . . . . . 138 9.3.7 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

9.4 Prolog and theorem proving . . . . . . . . . . . . . . . . . . . . . 141 9.4.1 Prolog terms . . . . . . . . . . . . . . . . . . . . . . . . . 142 9.4.2 Lexical analysis . . . . . . . . . . . . . . . . . . . . . . . . 142 9.4.3 Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 9.4.4 Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 9.4.5 Backtracking . . . . . . . . . . . . . . . . . . . . . . . . . 146 9.4.6 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 9.4.7 Theorem proving . . . . . . . . . . . . . . . . . . . . . . . 149