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

Lists

Basic List Operations

These basic operations

  1. Create
  2. Prepend
  3. Iterate through
  4. Look up
  5. Sub-chunks

Are fairly intuitive, I think. You can figure them out by studying this one example:

printl.hs
1-- create a list
2a = [1,2,3,4]
3 
4-- prepend an element to a list
5b = 0 : a
6 
7-- iterate through a list and print
8foo [] = putStr ""
9foo a =
10    do
11        putStrLn(show(head a))
12        foo(tail a)
13 
14main = 
15    do
16        putStrLn(show(b))
17        foo b
18        -- look up element 3 of the list
19        putStrLn("b!!3="++show(b!!3))
20        putStrLn("b!!0 to b!!3="++show(take 4 b))
21        putStrLn("tail b="++show(tail b))
$ ghc --make printl.hs
[1 of 1] Compiling Main             ( printl.hs, printl.o )
Linking printl ...
$ ./printl
[0,1,2,3,4]
0
1
2
3
4
b!!3=3
b!!0 to b!!3=[0,1,2,3]
tail b=[1,2,3,4]

Sequences

Haskell has a convenient shorthand for making sequential lists of integers:

seq.hs
1main = putStrLn(show([3..9]))
$ ghc --make seq.hs
[1 of 1] Compiling Main             ( seq.hs, seq.o )
Linking seq ...
$ ./seq
[3,4,5,6,7,8,9]

The construct [3..9] means make a list of integers beginning with 3 and ending with 9, inclusive.


List Comprehensions

Haskell has a simple notation for generating a list from a function or functions:

comp.hs
1main = putStrLn(show([ n*n | n <- [0..10]]))
$ ghc --make comp.hs
[1 of 1] Compiling Main             ( comp.hs, comp.o )
Linking comp ...
$ ./comp
[0,1,4,9,16,25,36,49,64,81,100]

This describes a list that is constructed from a sequential list that goes from one to 10.

This is another way to write a "loop" in Haskell.

You can also put a conditional to exclude certain values. In this example, we exclude n == 5.

comp2.hs
1main = putStrLn(show([ n*n | n <- [0..10], n /= 5]))
$ ghc --make comp2.hs
[1 of 1] Compiling Main             ( comp2.hs, comp2.o )
Linking comp2 ...
$ ./comp2
[0,1,4,9,16,36,49,64,81,100]

Note that /= means "not equals."


Infinite Lists

Because Haskell evaluates expressions lazily, it is possible to define an infinite list and lookup it's elements. Haskell is smart enough to only compute as much of the list as you actually use.

infl.hs
1infl1 = 0 : 1 : [ 1+n | n <- tail infl1 ]
2infl2 = 0 : 1 : 2 : [ 1+n | n <- tail infl2 ]
3 
4main =
5    do
6        putStrLn(show(take 10 infl1))
7        putStrLn(show(take 10 infl2))
$ ghc --make infl.hs
[1 of 1] Compiling Main             ( infl.hs, infl.o )
Linking infl ...
$ ./infl
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,2,3,3,4,4,5,5]

The way Haskell seems to process this is to build the list up in steps. For infl2, we begin with the list [0,1,2]. It takes the tail, [1,2], and feeds that into the list comprehension generating the new elements [2,3], making the list [0,1,2,2,3]. Next it processes the new pieces of the array using the list comprehension generating [3,4], and making the complete list [0,1,2,2,3,3,4].

Here we use an infinite array to store all the Fibonnaci numbers. This is more efficient than our recursive function definition earlier.

fiba.hs
1fib = 1 : 1 : [ fib!!(n-1)+fib!!(n-2) | n <- [2..]]
2 
3main = putStrLn(show(take 10 fib))
$ ghc --make fiba.hs
[1 of 1] Compiling Main             ( fiba.hs, fiba.o )
Linking fiba ...
$ ./fiba
[1,1,2,3,5,8,13,21,34,55]

Note the use of [2..] to create a list of integers from 2 to infinity.