Welcome to CS.SE!
I cannot present the underlying algorithms themselves, just the concepts they use.
The normal form (NF) of some expression is basically its result, like
\ x -> 1 + x.
The weak head normal form (WHNF) is some expression which is one of three:
- A lambda:
\ x -> x + 5 * 6. The NF is
\ x -> x + 30.
- Data constructor:
(3, 2 + 5). The NF is
(3, 7). It could be partially applied:
(,) (2 * 5). The NF is
\ x -> (10, x).
- Partially applied built-in function:
(+) (2 * 8). The NF is
\ x -> 16 + x.
As one can see, WHNF generalizes NF.
So, what have graphs got to do with this? Well, one can understand
f e1 e2 ... en as a graph with a node
f which is connected with
n other nodes:
e1, e2, ..., en. Evaluation is the reduction of the nodes1. To show why this is actually very cool I am going to use a picture which I stumbled upon on Wikipedia and HaskellWiki not so long ago:
So, what is going on in this picture? The idea is simple: when working with lists, we have a binary operation
(:) and some initial value
. When we are folding, we have a binary operation
f and some initial value
z. The data structure does not change - only the nodes of our graph. To get the complete result (NF, that is), we reduce the graph.
(Please note that the initial
1 : 2 : 3 : 4 : 5 :  could not have been reduced any further; in (pseudo-)haskell the lists are:
data [a] = a : [a] | .)
1Some technical details on evaluation (the basic tools to manipulate graphs, just as you have asked). The programmer might force the reduction.
seq x y forces its first argument to the WHNF and returns the second (being basically
flip const magically strict in the first argument). More formally:
seq x y means that whenever
y is evaluated to weak head normal form,
x is also evaluated to weak head normal form.
If the programmer wants to do the same for the NF, they should take a look at