Miranda: The Craft of Functional Programming

  • Authors:
  • Simon Thompson

  • Affiliations:
  • -

  • Venue:
  • Miranda: The Craft of Functional Programming
  • Year:
  • 1995

Quantified Score

Hi-index 0.00

Visualization

Abstract

From the Book:PREFACE:Over the last fifteen years, functional programming has come of age. It has been used successfullyon a number of substantial projects. A variety of robust and efficient implementations offunctional languages have been developed. A functional language is taught as the firstprogramming language in many university computing courses, and at a later stage in most others.Before examining the reasons for its popularity, we give a short overview of functionalprogramming. What is functional programming? Functional programming is based on the simplest of models, namely that of finding the value of an expression. This we meet in our first years at school, when we learn to work out expressions like 8(3-2) by evaluating the two halves, 8 and (3-2), and adding the results together. Functional programming consists of our defining for ourselves functions like which we can then use to form expressions. We define these functions by means of equations, like addD a b = 2*(a) (1)which we use to calculate the value of an expression like addD 2 (addD 3 4).We evaluate this using (1) to replace occurrences of addD with their values, so that we can write down a calculation of its valueaddD 2 (addD 3 4)= 2*(2 (addD 3 4))= 2*(2 2*(3 4))= 32As well as using (1) to calculate, we can read it as a logical description of how the function addD behaves on any values a and b; on the basis of this we can reason about how it behaves. For instance, for any valuesa and b,addD a b = 2*(a) = 2*(b) = addD b aThis equation holds for all possible input values, in contrast to the information we gain from testing a function at a selection of inputs.On top of this simple model we can build a variety of facilities, which give the functional approach its distinctive flavour. These include higher-order functions, whose arguments and results are themselves functions; polymorphism, which allows a single definition to apply simultaneously to a collection of types; and infinite data structures which are used to model a variety of data objects. The Miranda language also has support for larger-scale programming, including user-defined algebraic types, such as lists, trees and so on; abstract data types and modules. These contribute to separating complex tasks into smaller sub-tasks, making the components of systems independent of each other, as well as supporting software re-use.Why learn functional programming?As a vehicle for learning programming principles a functional language like Miranda has many advantages. As we saw, it is based on a simple model which allows us both to perform evaluation by hand, animating our understanding, and to reason about how the programs behave. In this text, both calculation and reasoning have a central part.The language is higher-level than most. For example, lists in Miranda are defined directly as a recursive type, and so there is no need to examine their implementation by pointers; indeed the functional language can be used as a design language for the imperative implementation. A second instance is provided by polymorphism; generic definitions are introduced with almost no overhead, in contrast to object-oriented languages such as Modula-3 and C.Moreover, the language supports larger-scale programming by means of its abstype and module mechanisms. Again, these have advantages over traditional languages: module versions are kept consistent automatically, for example.For these reasons we see Miranda as providing a sound introduction to programming principles, which can be complemented later by learning an imperative language.Equally valuable is to learn Miranda after using a more traditional language, since it canbroaden our view of the programming process as well as illustrating many software engineeringtechniques of general worth.Our approachThe material is grouped into three parts. In the first, we build a foundation, focusing on programming over basic types and lists, using first-order, non-polymorphic programs. Only when readers are fluent with the basics of functions, types and program construction do we look at the twin ideas of higher-order functions and polymorphism, which together give modern functional programming its distinctive flavour and power. Based on this, in the final part we look at larger-scale programming, supported also by an exploration of user-defined types, modules and lazy evaluation, and concluding with an analysis of program efficiency.As we saw above, we can write down calculations of expressions which use our defined functions, and also give proofs of various of their properties; both aspects are emphasised in the text (although it can be read independently of the material on proof).The crucial test of any programming text is whether it helps the reader to write programs. The book is called 'The Craft of Functional Programming'because it aims to give readers help with design of programs right from the start. At each stage we give advice on how functions, types or modules can be designed; in many cases we give a series of steps which aim to simplify the problem by breaking it down into simpler parts.Software of any importance will be modified during its lifetime; modification and re-use are emphasised when we discuss design, and in the final part of the book we look in particular at how libraries of polymorphic higher-orderfunctions can be re-used in many different contexts.The advice on design is supplemented by examples and case studies of varying size and complexity. Some, like the Huffman coding example, are free-standing; others, such as the library database, are re-visited a number of times to illustrate how new techniques can be applied to solve existing problems in different ways. This is important in linking together the different parts of the text, and in showing the variety of ways that any problem can be solved in Miranda.Finally, other case studies are introduced step-by-step. We first see the simulation example when we design algebraic data types in Chapter 9; next it provides realistic examples of abstract data types in Chapter 11, and finally we look at its top level in Chapter 13. Each aspect is separate; together they provide a substantial case study.Other aspects of our approach areThroughout the text, whenever a function is defined we give its type explicitly; the type forms the first and most important piece of documentation for any definition.Material is included on how to find proofs by induction; a template to help withsetting down the precise goals of an induction proof is included, as is a discussion of how to search for proofs about functions defined by equations.Appendices contain support material of various kinds. Common errors and error messages are collected in one; a glossary of programming terms commonly used in another. A third appendix lists a major part of the Miranda standard environment, a fourth examines how functional and imperative programming are linked and a fifth gives references for further reading. Finally, we give an overview of how to understand an unfamiliar function definition in Miranda.Over four hundred exercises of varying difficulty are included.Further support material which is be found on these World Wide Web pages. OutlineThe material is presented in three parts.Part I: Basic functional programmingThe aim of Part I is to introduce the basic tools of functional programming, and to give detailed guidance about how to build programs to solve particular problems. The technical material covers the basic types of Miranda: numbers, characters and Booleans, and the structured types of tuples and lists. Readers are introduced to function definitions involving guards, pattern matching and local definitions (where clauses) through a sequence of examples and sections. Throughout the introduction, details are given about how particular problems can be approached, to encourage students to develop their own solutions. Divide and conquer approaches, and top-down design are encouraged, and illustrated by means of examples including a small database, the production of supermarket bills, text processing and equation solving.To animate the programs, calculations of many examples are given, and readers are encouraged to try definitions out using these rewriting sequences. Integrated into this and the other Parts of the book are proofs of program properties. These are shown to be written to a template, in the cases of proof over numbers and lists, and many examples of graded difficulty are presented. More traditional approaches to software testing are also discussed and encouraged.The material in this Part is all first-order and non-polymorphic, deliberately. The aim of the Part is to give students a firm grounding on which they can make the abstractions which are the subject of the next Part. Part II: AbstractionThis Part forms the bridge between the material on basic functional programming, and the larger-scale programs of the final Part. The distinctive features of languages like Miranda are that they are higher-order, allowing functions as arguments to and results of other functions, and polymorphic with definitions applying to, for instance, all types of list rather than to lists of a single type.Higher-order and polymorphic functions are shown in this Part to be generalizations or abstractions of definitions familiar from the first Part: an example is themap function which applies an operation to every member of a list, irrespective of the operation (it is a parameter) and the type of the list.These technical ideas are introduced, and many of the examples of Part I arere-examined to show the general higher-order functions they exemplify or use. Libraries of general functions are developed, particularly over the polymorphic list type.The consequences of these ideas for software design and engineering are also examined: polymorphic higher-order functions are ideal candidates for re-use, and this is illustrated in the examples. The most technical Chapter of the book is the final one of this Part, where type checking is presented. This is necessary fully to exploit the features of the language, and also to understand the error messages which result when type checking fails. Program verification is a continuing theme here, and it is argued that theorems about general functions are re-usable in the same way as the functions themselves.Part III: Larger-scale programmingThe final Part of the book brings in the aspects of the language which support larger-scale programming: user defined concrete (or algebraic) types; modules; abstract data types. In introducing each of these, examples are given, together with general principles for design of systems which contain algebraic and abstract data types, as well as how systems are built top-down and bottom-up using modules. Amongst the examples we look at are the design and use of Huffman codes, the simulation of multiple queues (as might be found in a bank), finding the difference between two files, simple interactive programs and aspects of a simple arithmetic calculator.Lazy evaluation is also discussed here. After showing precisely what it means and how it works, the consequences it has for program development are explored. These include data-directed solutions, infinite data structures and interactive streams of input and output. Design principles for interactive programs are explored, and verification is addressed. The book concludes with an analysis of program behaviour, including the time taken and spaced used during evaluation. Examples from the text are re-visited, and various strategies for optimising program performance are introduced.Appendices, Bibliography and IndexThe appendices consist of: a comparison of functional and imperative programming; a glossary of commonly used and technical terms; sources for further reading; a discussion of common error messages, and their possible sources; an annotated listing of some of the built-in functions (the standard environment) and a reminder of the ways that unfamiliar definitions can be understood.Who should read this book?This text is intended as an introduction to functional programming for computer science and other students, principally at university level. It can be used by beginners to computer science, or more experienced students who are learning functional programming for the first time; either group will find the material is new, challenging and interesting.The book can also be used for self-study by programmers, software engineers and others interested in gaining a grounding in functional programming.The text is intended to be self-contained, but some elementary knowledge of commands, files and so on would be needed to use the Miranda system under Unix. Some logical notation is introduced in the text; this is explained as it appears. In the final Chapter it would be helpful to have an understanding of the graphs of thelog, n^2 and 2^n functions.To the teacherDepending upon a teacher's preferences and the context in which the course is taught, different routes through the book exist. The ordering of the text roughly corresponds to the first year course at the University of Kent, although the material here is much expanded from that given to our students. The book follows a linear progression, and it makes sense to follow it from Chapter 1 to Chapter 14; if time does not permit, a coherent course on smaller-scale programming is given by the first Part; together with the second, students will gain an additionalgrounding in the novelties of functional programming, whilst moreadvanced techniques are introduced in Part III.In Part III it is possible to pick and choose Chapters and Sections, rather than following the material page-by-page. The case studies illustrate the material, but a teacher could use his or her own examples rather than those of Huffman coding or relations and graphs, for instance. The calculator and simulation examples are distributed through Part III; another approach would be to delay them until all the supporting material has been studied.Integral to this course is reasoning about programs, but the material on proof in this text stands independently, and so a proof-free course could be given, omitting Chapters 3 and 5, and Sections 7.7, 9.6 and 13.7. It would also be feasible to give the proof material rather later than it appears in the text. Also standing alone is the material on program behaviour; this could be omitted if so desired.It is impossible to exclude some more technical Sections from the book, but some of this material could be deferred, mentioned in passing or given an informal gloss. In the first two Parts, Sections 5.4, 8.3 and 8.4 are potential candidates.Students should be encouraged to use the information in the Appendices. Depending upon their backgrounds, the comparison of functional and imperative programming can be illuminating in both directions; at the University of Kent we find that linking the treatment of lists in Miranda and in an imperative language is very helpful for students who are learning the intricacies of pointer-based linked listsAlso to be recommended to students is the glossary; both for beginners to programming, and to those familiar with other programming paradigms, some of the terminology of functional programming is either new or consists of common terms used in a slightly different way from the norm. In either case, students should be encouraged to look up words they are unsure about. Also in the appendices are a full treatment of common errors, a reminder of the different ways to understand an unfamiliar function and sources for further reading.Resources for teachers, including the program text, sample solutions to the exercises and a teachers' guide are available on these World Wide Web pages.AcknowledgementsI would first like to thank my colleagues in the functional programming groupat the University of Kent.I have learned a tremendous amount from them, both about functional programming itself, and abouthow best to teach it.The reviewers of this text have provided invaluable help in finding errors and infelicites of presentation. Each of them provided different, but useful, feedback; grateful thanks therefore to Howard Bowman, Allan Grimley, Steve Hill, Ron Knott, Janet Linington, Iain MacCallum, Phil Molyneux and David Turner. Needless to say, any errors which remain are the author's responsibility.I am very grateful to Nick Riding for the loan of a portable PC on which many of the programs were developed, and to Barry Dean for his help in getting the PC to use Linux.In preparing these Web pages I have had invaluable comments and advice fromIan Cooper, Sally Fincher, Louise Heery, Richard Jones,Bob O'Keefe and Dan Russell.Final and most heartfelt thanks to Jane, Alice and Rory, for their unfailing support over the last year during which this book has come to fruition.--Simon Thompson 0201422794P04062001