Table of Contents
FIXME
FIXME: goals of this book. The emphasis will be practical: on using FP and Haskell to design, write, and fix real programs. The audience is people who can already program. There will be plenty of examples and exercises.
As a new Haskell programmer, you'll inevitably end up writing quite a bit of code by hand for which you could have used a library function or programming technique, had you but known of its existence. You'll also run into instances where a cherished feature of another language isn't available; or a library function you just know ought to be there isn't; or occasionally something will be put together in such a way that you know it could have been improved upon. If you accept the inevitability of the occasional surprise, difficulty, and shortcoming, you'll have much more fun learning Haskell than if you step in expecting perfection, only to trip up within a few days or weeks of getting started.
FIXME
FIXME: roots in research
FIXME: perception that it's not practical; this book will refute
In a language such as C, you might see a statement such as
x = x * 5 + y
. This statement modifies the place
in memory where x
is stored and changes it to
the result of the computation. Many, many other languages work the
same way. For instance, a Python programmer might say
mylist[5] = 10
, which will set the value of the
fifth element in mylist
to 10
.
In Haskell, all data is immutable -- it cannot be modified. If you need to modify an element of a list, you'll get a copy of the original list returned -- except for the one changed element. Then you will probably ignore the original list.
You might start to think that this could be slow to execute. That turns out not to be the case, as the Haskell compiler can optimize this sort of usage quite well.
It also turns out to be a powerful way to code: it makes it easy to "chain" functions together, the result of one being taken as input to another. POSIX programmers can think of this as similar to pipes in the shell.
There's another benefit of this: a lack of side-effects. A common source of bugs in many languages is calling a function that does something unexpected: modifies a global or a class variable, for instance. Later code then could behave in unexpected ways.
Some languages have partial immutability. For instance, strings are immutable in Java; if you want to add something to a string in Java, you combine two strings and the result is a new string -- not a modification of an existing string. Haskell takes immutability much farther. The only exception in Haskell deals with I/O, which is encapsulated in a special way; this will be discussed later.
Chances are that most languages you've used before have the notion
of for
or while
. These
tools are often used for performing actions a fixed number of times,
for doing something over and over until some state changes, or for
simply repeating infinitely until the program is killed.
Haskell has no for
or while
.
That's not an oversight; Haskell doesn't have for
or while
because Haskell doesn't need them.
Let's consider a simple program to output the numbers from 1 to 10 on the screen. A C programmer might work this way:
#include <stdio.h> void main(void) { int i; for (i = 1; i <= 10; i++) { printf("%d\n", i); } } #include <stdio.h> void main(void) { int i; for (i = 1; i <= 10; i++) { printf("%d\n", i); } }
This assigns the value 1 to i
on the first time,
then modifies that place in memory each time through, incrementing
i
until it reaches 10.
A Haskell programmer could simply say:
main = mapM_ print [1..10]main = mapM_ print [1..10]
The [1..10]
generates an lazy
list of numbers from 1 to 10. Each element will be created only when
demanded. You could just as easily say
[1..1000000]
and you'll use no more memory.
The call to mapM_
takes an I/O function and runs
it once for each element passed in. So we never need to directly
modify memory, yet we can express the entire operation more compactly
than in C.
A Haskell programmer could also express this without using
mapM_
like this:
main = printit 1 printit 11 = return () printit x = do print x printit (x + 1)main = printit 1 printit 11 = return () printit x = do print x printit (x + 1)
This function uses recursion and pattern matching to achieve the result. You'll learn more about these techniques in the next few chapters in the book.
While Haskell's approach may seem foreign at first, we think that you'll grow to appreciate its speed and robustness as you learn about it. You'll see why Haskell doesn't have loops because loops would be less useful!
FIXME: Mention the Haskell environment that the book assumes (GHC 6.6+). Describe how to obtain and install it. Refer to alternative Haskell implementations (Hugs), but leave any detail to an appendix.