Functional Programming Laboratory 1 Programming Paradigms
Total Page:16
File Type:pdf, Size:1020Kb
Intro 1 Functional Programming Laboratory C. Beeri 1 Programming Paradigms A programming paradigm is an approach to programming: what is a program, how are programs designed and written, and how are the goals of programs achieved by their execution. A paradigm is an idea in pure form; it is realized in a language by a set of constructs and by methodologies for using them. Languages sometimes combine several paradigms. The notion of paradigm provides a useful guide to understanding programming languages. The imperative paradigm underlies languages such as Pascal and C. The object-oriented paradigm is embodied in Smalltalk, C++ and Java. These are probably the paradigms known to the students taking this lab. We introduce functional programming by comparing it to them. 1.1 Imperative and object-oriented programming Imperative programming is based on a simple construct: A cell is a container of data, whose contents can be changed. A cell is an abstraction of a memory location. It can store data, such as integers or real numbers, or addresses of other cells. The abstraction hides the size bounds that apply to real memory, as well as the physical address space details. The variables of Pascal and C denote cells. The programming construct that allows to change the contents of a cell is assignment. In the imperative paradigm a program has, at each point of its execution, a state represented by a collection of cells and their contents. This state changes as the program executes; assignment statements change the contents of cells, other statements create and destroy cells. Yet others, such as conditional and loop statements allow the programmer to direct the control flow of the program. An algorithmic problem defines a function from its instances to solutions. An imperative program solves an algorithmic problem by going through a sequence of states, where the initial and final states represent a problem instance and its solution, respectively. The paradigm is called ‘imperative’ since it uses statements to tell the ma- chine what to do. It is closely related to the structure and operation of comput- ers as we know them today: cells abstract over memory, statements abstract over machine-level instructions. This simple idea is developed further by additional abstractions: Functions abstract operations, and can be used in expressions; procedures abstract instr- cutions; composite data types provide useful structures. Intro 2 int factorial(num:int){ factorial(num:int) is int fact; if (num = 0) then 1 for (fact = 1; num > 0; num--) else num * factorial(num -1) fact *= num; return fact; } (a) (b) Figure 1: Two programs for computing the factorial In object-oriented programming, objects, package together data and opera- tions. The use of objects, classes, and inheritance has a major impact on the programming style; the object-oriented programming is considered a separate paradigm. Yet, like the imperative paradigm, it is based on the notion of state. A program execution still corresponds to a sequence of states. 1.2 Functional programming Functional programming views programming as the activity of defining func- tions. An algorithmic problem is solved by defining a function that maps each instance to its solution. Recursive definition of functions is allowed and encour- aged. Functions and a language’s built-in operations can be used in expressions, where they are applied to values. A program is a collection of definitions, mostly of functions, but also of data values. In this approach, there are no notions of state and state change, hence no primitives for state change (but see below). Figure ?? shows, in part (a), an imperative program for the computation of the factorial, written in the imperative language C, and in part (b), a functional program for computing this function in pseudo-code. In the first program, the computation is achieved by an iteration — a control structure explicitly designed for controlling state changes. In the second, we have a definition as a mathematician would write it. A statement that affects the state, as in the imperative style, is said to have a side-effect. Assignment and procedure calls have side-effects. Input/output statements are also considered to have side-effects; here the state is extended to encompass the input/output devices. In the pure functional paradigm expres- sions, including function calls, are evaluated only for their value. There are no cells, programs have no state, hence in pure functional programming there are no side-effects. Consequently, there are also no conditional and loop statements. (Do you see why?) The paradigm as described so far, without cells and side-effects, is often called pure functional programming. It is possible to include cells and I/O in a functional language, and then, if one still follows the methods of functional programming, one is said to program in a (mostly) functional style. Intro 3 Important: In this laboratory we stick to pure functional programming, almost until the end of the semester. Since imperative programs also have expressions, functions, and recursion, it might seem functional programming is at a disadvantage. This is not so. Other ideas in functional programming compensate for the absence of state. Probably the most important is the use of higher-order functions. These are functions that accept functions as parameters or that return functions (or both). Imperative languages restrict their use, whereas functional languages place no restrictions. This provides much of the power and elegance of the functional paradigm. 1.3 Why study functional programming? Imperative languages such as Pascal and C are called high-level; functional (and logic programming) languages are often called very high-level, or declar- ative, since a programmer declares what needs to be done, rather than how it should be done. Functional programming is further removed from the details of the underlying hardware architecture than imperative programs. Ideas such as higher-order functions add a lot of power and elegance to programming; understanding them enhances the problem solving and abstraction abilities of programmers. Among the often-cited advantages of functional programming: • Functional programs are more elegant, more abstract, than corresponding imperative programs. • It is easier to reason about them, e.g., prove their correctness. • Pure functional programs are transparent: Since a functional program has no side-effects, one can always replace a (pure) function definition by another that is provably equivalent to it. That allows big-scale optimiza- tions of functional programs. Such optimizations are next to impossible for imperative programs. These advantages relate to well-written functional programs; clearly, it is pos- sible to write ill-structured programs in any paradigm. Also, these advantages become manifest in the development of non-trivial programs. This lab only makes a modest step to illustate them, since the programs are far from being large. Intro 4 Advice: Although the underlying idea of ‘define functions’ is simple, programmers used to imperative programming suffer a cultural shock when introduced to the functional paradigm. Mastering it takes time and experience. Have patience! 2 The Language Scheme Many languages support functional programming. Haskell and ML are powerful languages, rich in constructs that support well-structured large-scale program- ming. But, for our purposes, the richness is a disadvantage. We prefer a simple language, that can be learned easily, so we can concentrate on the ideas. Scheme is a dialect of Lisp. It is a rather small language, with a uniform and simple syntax. It has some sophisticated constructs that give it a lot of power, but we do not use them. We use Scheme mainly for its simplicity. That allows us to concentrate on the ideas. Most modern languages do compile-time type checking, while Scheme does run-time type-checking. This is another reason for the choice of Scheme: To let the student have a taste of this approach. Warning: Scheme is not a pure functional language: it has cells and assign- ments. For most of the lab we present an purely functional view of the language, by carefully restricting the operations we allow to use. We present cells and as- signments only in the very last part of the lab. Do not use language constructs that have not been described/mentioned in the reading sheets! Bibliography • H. Abelson, G.J. Sussman, with J. Sussman, Structure and Interpretation of Computer Programs, (2nd ed.) The MIT Press, 1996. (The 1st edition is also ok for this lab.) This book, very well known, as an introduction to CS using Scheme, is called in this lab the Blue Book. Many examples and exercises are taken from this book. It is highly recommended for reading. The material we cover is in the first three chapters. • We use the DrScheme system, developed at Rice University, as part of the PLT project. Their home page contains a lot of material about Scheme. See http://www.cs.rice.edu, and http://www.cs.rice.edu/CS/PLT. Some of it, such as the DrScheme binaries for installation, and some tutorials, are available at this course site. • H. Abelson et al, Revised 5 Report on the Algorithmic language Scheme. This is the IEEE standard of Scheme. DrScheme has online the R5R, which is the fifth and final definition of the language. You should make an effort to read some sections. •The course home page has references to a lot of material about Scheme, its implementations and uses..