First, Haskell's API has no preference between WHNF and NF : it provides both seq and deepseq.
So maybe your question is why does WHNF exist at all ? Simply put, it is the least evaluation possible. A full calculation would go like this :
- unevaluated (thunk)
- WHNF : outmost constructor known
- deeper evaluations
- NF : the value is completely calculated
However, Haskell is lazy, it tries to do as little calculation as possible to get its final result. For example, to compute the length of a list l, Haskell must know whether l is [] or _ : t. Then recursively the same question about t. Therefore it only needs the number of constructors : before the final constructor []. This is by definition successive WHNFs, which only extract the outmost constructor of a value.
The outmost constructor is the minimal information you must know about a value to actually do something with it, such as selecting a branch in a case of, like in length just above. That is the interest of WHNF over NF.
Note that for simple types like Int, each value is its own constructor (2, -7, 15, ...), so WHNF = NF for them.
Finally, you rarely care about all this : it is the compiler's job to execute your program as fast as possible. In the evaluation of foldl (+) 0 [1..100], if you try to figure out when the list [1..100] finishes in NF you will be completely wrong. The compiler transforms that fold into this tail-recursive loop
let sum ret i =
if i == 100 then 100 + ret
else sum (ret+i) (i+1) in
sum 0 1
which means it doesn't evaluate the list at all. Unfortunately there are situations where the compiler cannot know that a value will always be in NF when the final result of the program is produced. Then it is a waste of time and RAM to keep it unevaluated (forming a thunk has a cost) : better deepseq it right from the start. Or your compiler may have performance bugs and you must help it with seq or deepseq. A profiler will tell you what parts of your code are slow.