1. The Easy Part
  2. Higher Order Functions
  3. Types
  4. Lists
  5. Complex Data Objects
  6. File IO
  7. Classes
  8. Monads
  9. List Monads
  10. Shared Transactional Memory

The Easy Part

Hello, World!

The first step in learning any programming language is the 'hello, world' program. Here's the Haskell version.

hello.hs
1main = putStrLn("Hello, world!")
$ ghc --make hello.hs
[1 of 1] Compiling Main             ( hello.hs, hello.o )
Linking hello ...
$ ./hello
Hello, world!

In Haskell, as in C, you have to define "main" in order to create an executable. Haskell, however, does not use the curly braces.


Numbers

math1.hs
1main = putStrLn(show(3+4*9))
$ ghc --make math1.hs
[1 of 1] Compiling Main             ( math1.hs, math1.o )
Linking math1 ...
$ ./math1
39

To obtain numeric output, it is necessary for us to convert the result to a string in order to print it. We do that with show().


String Concatenation

math2.hs
1main = putStrLn("3+4*9="++show(3+4*9))
$ ghc --make math2.hs
[1 of 1] Compiling Main             ( math2.hs, math2.o )
Linking math2 ...
$ ./math2
3+4*9=39

String concatenation is accomplished with the ++ operator.


Let Blocks

math3.hs
1main = 
2    let
3        x = y+2
4        y = 3
5        z = y+1
6    in
7        putStrLn("x="++show(x)++", z="++show(z))
$ ghc --make math3.hs
[1 of 1] Compiling Main             ( math3.hs, math3.o )
Linking math3 ...
$ ./math3
x=5, z=4

In order to set variables you need to use a "let" block. You'll notice that both x and z depend on y, but y is defined after x and before z. This is possible because Haskell is a functional language. That means that variables are defined when they are set, and never change in value. That means that order of assignment is not important.

The other thing to notice is that blocks are defined by whitespace, just as they are in python. If you prefer to use curly braces, like C or java, this is possible.


Defining functions

fib.hs
1fib n = 
2    if n < 2 then
3        1
4    else
5        fib (n-1) + fib (n-2)
6 
7main = putStrLn(show(fib 10))
$ ghc --make fib.hs
[1 of 1] Compiling Main             ( fib.hs, fib.o )
Linking fib ...
$ ./fib
89

Here we see our first example of a function that takes an argument. In this case, it is the inefficient implementation of the Fibonacci function.

One important thing to note here is that function names are never capitalized. Capitalized identifiers must refer to type names in Haskell.

fib2.hs
1fib 0 = 1
2fib 1 = 1
3fib n = fib (n-1) + fib (n-2)
4 
5main = putStrLn(show(fib 10))
$ ghc --make fib2.hs
[1 of 1] Compiling Main             ( fib2.hs, fib2.o )
Linking fib2 ...
$ ./fib2
89

Here we see a slightly prettier version of the Fibonacci using what are called "patterns" to handle the condition where n < 2.

fib3.hs
1fib n
2  | n==0 = 1
3  | n==1 = 1
4  | otherwise = fib (n-1) + fib (n-2)
5 
6main = putStrLn(show(fib 10))
$ ghc --make fib3.hs
[1 of 1] Compiling Main             ( fib3.hs, fib3.o )
Linking fib3 ...
$ ./fib3
89

Another variant, called "guards" can be used to do the same thing.

One of the characteristics of the function defined above is that it is referentially transparent. This means that if a function is called twice with the same arguments, the compiler is free to replace the second function call with the value obtained by the first.

Furthermore, there is no way to determine what order fib(n-1) and fib(n-2) will evaluate. The compiler is free to attempt either computation first.


Lazy Evaluation

fib4.hs
1fib 0 = 1
2fib 1 = 1
3fib n = fib (n-1) + fib (n-2)
4 
5main =
6    let
7        f1000 = fib 1000
8        hi = putStrLn("hello")
9    in
10        putStrLn(show(fib 10))
$ ghc --make fib4.hs
[1 of 1] Compiling Main             ( fib4.hs, fib4.o )
Linking fib4 ...
$ ./fib4
89

Haskell is lazy. That means, it doesn't compute a value if it does not have to. In this case, neither the variables f1000 nor hi are computed. Fib3 evaluates as quickly as fib2, and does not print the word "hello."


More than one line of output

several.hs
1main =
2    do
3        putStr("Line 1: ")
4        putStrLn("one line of output")
5        putStr("Line 2: ");
6        putStrLn("another line of output")
7        putStr("Line 3: ");
8        putStrLn("another line of output")
$ ghc --make several.hs
[1 of 1] Compiling Main             ( several.hs, several.o )
Linking several ...
$ ./several
Line 1: one line of output
Line 2: another line of output
Line 3: another line of output

In order to do more than one line of output, you need to use a "do" block. It is a special construct for stitching together a number of output commands. Inside a do block, each command will execute independently and in order. This gets around all the limitations imposed by functional programming.

Note also the use of putStr() vs. putStrLn(). The putStr() function does not output a carriage return / line feed.


Loops

loop.hs
1again nlo nhi =
2    if nlo == nhi then
3        putStrLn("n = "++show(nlo))
4    else
5        do
6            putStrLn("n = "++show(nlo))
7            again (nlo+1) nhi
8main = again 1 10
$ ghc --make loop.hs
[1 of 1] Compiling Main             ( loop.hs, loop.o )
Linking loop ...
$ ./loop
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9
n = 10

Here we put all the information above together to teach you how to write a simple loop. Because the if statement was outside the do block we were forced to write the putStrLn("n="...) line twice.

loop2.hs
1again nlo nhi =
2    let
3        putn = putStrLn("n = "++show(nlo))
4    in
5        if nlo == nhi then
6            putn
7        else
8            do
9                putn
10                again (nlo+1) nhi
11main = again 1 10
$ ghc --make loop2.hs
[1 of 1] Compiling Main             ( loop2.hs, loop2.o )
Linking loop2 ...
$ ./loop2
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9
n = 10

That's still not elegant. However, there is another option. Case statements are allowed inside do blocks:

loop3.hs
1again nlo nhi =
2    do
3        putStrLn("n = "++show(nlo))
4        case (nhi-nlo) of
5            0 -> putStr ""
6            otherwise -> again (nlo+1) nhi
7 
8main = again 1 10
$ ghc --make loop3.hs
[1 of 1] Compiling Main             ( loop3.hs, loop3.o )
Linking loop3 ...
$ ./loop3
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9
n = 10

The tricky thing about case statements is that their patterns must be compile time constants. You can't use "nhi ->" in your code. That's why we chose to switch on (nhi-nlo) rather than nlo.

Of course, we could have simply inverted the if and do blocks like so:

loop4.hs
1again nlo nhi =
2    do
3        putStrLn("n = "++show(nlo))
4        if nlo == nhi
5            then putStr ""
6            else again (nlo+1) nhi
7 
8main = again 1 10
$ ghc --make loop4.hs
[1 of 1] Compiling Main             ( loop4.hs, loop4.o )
Linking loop4 ...
$ ./loop4
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9
n = 10

Recursion

If you are a C programmer, using recursion may worry you. The following C program would probably seg fault because it would completely exhaust the stack:

inf.c
1#include <stdio.h>
2 
3void inf(int n) {
4    if(n == 0) {
5        printf("done\n");
6        return;
7    }
8    inf(n-1);
9}
10 
11int main() {
12    inf(10000000);
13    return 0;
14}

The equivalent Haskell program, however, is smart enough not to grow the stack because it knows that it is not really necessary since the above code is logically equivalent to a loop.

inf.hs
1inf 0 = putStrLn("done")
2inf n = inf (n - 1)
3main = inf 10000000
$ ghc --make inf.hs
[1 of 1] Compiling Main             ( inf.hs, inf.o )
Linking inf ...
$ ./inf
done

Changing State

If you still want think imperatively, you can just use a function call whenever you normally use a state change.

statesc.c
1#include <stdio.h> 
2 
3void foo(int a) {
4    printf("a=%d\n",a);
5    a ++;
6    printf("a=%d\n",a);
7}
8 
9int main() {
10    foo(1);
11    return 0;
12}
$ gcc -o statesc statesc.c
$ ./statesc
a=1
a=2
states.hs
1foo2 a =
2    putStrLn("a="++show(a))
3 
4foo a =
5    do
6        putStrLn ("a="++show(a))
7        foo2 (a+1)
8 
9main = foo 1
$ ghc --make states.hs
[1 of 1] Compiling Main             ( states.hs, states.o )
Linking states ...
$ ./states
a=1
a=2

These loop and state examples probably make you think that Haskell is a cumbersome difficult language -- but really, this is not illustrative of the Haskell way of doing things. It is more like the C way of doing things shoe-horned into Haskell.

But hey, we have to start somewhere.