Functional versus Imperative Hans Georg Schaathun November 5, 2008 These notes will be (were) discussed Friday 31 October, at the start of the lecture. They are prepared in response to a series of questions and problems arising in the lab sessions. 1 Java vs. Haskell 1. What is the difference between (programming in) Java and Haskell? 2. What is the similarity between (programming in) Java and Haskell? Haskell isn't real programming like Java; it is like using interactive software such as a calculator. The hugs interpreter provides a simple tool to test components of new programs. You should look at the programming that we do as the creation of small building blocks which are not assembled yet. It is a disadvantage of compiled programming languages that testing becomes hard. In order to test the basic building blocks, you have to create separate programs for each function to be tested. These programs have no user value. When we have an interactive interpreter (hugs), testing is simpler. You can evaluate any function, on the command line, without the creation of a complete program. 1.1 Large programs The purpose of University modules is not to teach particular languages, but to teach universal programming techniques which can be used in software development. Large systems will employ more than one technique. Real software systems are composed of many different modules doing different tasks. There will be • I/O functions communicating with the user • Computational functions, calculating information required from data stored 1 • Data processing functions (sorting, searching) • I/O functions reading and storing data on disk Your java module has started with user oriented I/O. Imperative languages are good and easy to use for I/O, so this makes sense. Furthermore, since you don't have an interactive interpreter, you need I/O to be able to verify that the function or program works as intended. In this module, we have started with computational functions. The interpreter allows us to test and debug these functions, without learning to do I/O. Functional programming techniques are very efficient and practical for this kind of computations, and they produce very readable code. I/O on the other hand, is hard to do in functional languages. In the future, when you start creating complex systems, you will see that techniques from your Java module applies to Haskell, and vice versa. You will need different tech- niques for different parts of the system, and put it all together. 2 Modules The test script for Coursework 1 has introduced the concept of modules. You do not need to know anything about modules yet, but since you have seen them in operation, I shall explain it. Modules roughly correspond to (abstract) classes in Java. They are collections of functions which can be imported in other programs or modules. In hugs, you are allowed to load Haskell files which do not explicitely declare a module. In these cases, hugs will treat the file as a module called Main. However, such files cannot be imported by other modules or Haskell files. In order (explicitely) to declare a module, you simply need a line of the following form. • module Ex2 where The file name (Ex2.hs) has to match the module name (Ex2). When the module is explicitely declared, it can be included in another file. My test script for Coursework 1 has the line import Ex2 to import your file. 3 The Coursework 1 Test Program The test program for Coursework 1 is a standalone program. It can be run by the command • runhugs test2 directly in the terminal window. The runhugs program is a non-interactive version of the hugs interpreter. It does not give you a prompt, instead it runs the Haskell program from the given file. It is also possible to compile the test2.hs program using the Glasgow Haskell Compiler (ghc). We shall have a look at this in Week 7. My test script is a standalone program because it 2 int factorial ( int n ) { int i, r=1 ; for ( i=1 ; i <= n ; ++i ) { r = r*i ; } return ( r ) ; } Figure 1: A factorial function using loops in C/Java. int factorial ( int n ) { int r=1 ; for ( ; n > 1 ; --n ) { r = r*n ; } return ( r ) ; } Figure 2: A factorial function using loops in C/Java, counting down. This avoids the extra variable i needed when the loop is counting up. 1. Declares a module called Main. 2. Includes a function called main. All that runhugs does is to load the module and evaluate main. 4 Recursion vs. Loops Recursion is the single most important topic in this module. As you have now learnt the for loop in Java, it may be useful to compare loops and recursion. • How would you create a factorial function in Java? int factorial ( int n ) { if ( n <= 1 ) { return (1) } else { return ( n*factorial(n-1) ) ; } } Figure 3: A factorial function using recursion in C/Java. 3 factorial :: Int -> Int factorial n | n <= 1 = 1 | otherwise = n*(factorial (n-1)) } Figure 4: A factorial function using recursion in Haskell. Figures 1 and 2 shows two ways of doing this using loops, making one multiplication at each step. Loops is an imperative technique which is not available in functional languages. At the heart of the for loop is the index variable (i in Figure 1) which is updated for each iteration. This variable creates a variable state, and the expression r = r*i is not transparent; it depends on the state of the machine. Haskell requires that any expression takes the same value every time it is evaluated. Recursion can be used in Java. Figure 3 shows a recursive C/Java function to calculate the factorial. This is a purely functional approach, with referential transparency. Inside the scope of the function, n is constant (given as an argument). There are no variables which are updated. Two calls of factorial(n) would always give the same answer (independent of state). This example illustrates that functional techniques applies in Java. In this simple example there is little practical difference between loop and recursion. Both are readable and efficient. In more complex examples, one or the other may be better. Returning to the Haskell code in Figure 4, we see an almost perfect match between the syntaxes of Java/C and of Haskell. 4