using less stack

John Hughes rjmh@cs.chalmers.se
Thu, 21 Mar 2002 10:18:21 +0100 (MET)


	\begin{rant}

	I think the whole seq thing is very confusing for beginning/intermediate
	haskell programmers.  I was very confused for a long time as to why

	> (unsafePerformIO (putStrLn "Hello")) `seq` 5

	would print "Hello", but

	> [unsafePerformIO (putStrLn "Hello")] `seq` 5

	would not. ...  I would almost prefer if the semantics of `seq` were instead those
	of rnf or deepSeq, so long as either (a) we were allowed to derive DeepSeq
	or NFData, or (b) if the compiler would just do it itself.  Yes, this
	would cut down on the expressions which we could `seq`, but does that
	really matter. ...

	\end{rant}

Well, seq isn't there to enable sequencing of unsafe IO. It's there to enable
space optimisation. When heap profiling was first developed for Haskell, it
fairly soon became clear that fine control of evaluation order was often the
key to fixing space leaks. (Note: "fine control" includes making things lazier
at times via ~ patterns, but also making things stricter at times). Seq is
designed so that you can take ANY expression and move its evaluation where you
want. If we only had deepSeq, we could only move an expression together with
all of its subexpressions -- not good if a lazy list needs to be forced a
little earlier! As for putting seq in a class excluding functions, that would
mean that if you happened to use a function to represent an abstract data
type, you would suddenly be unable to control the evaluation order of programs
using that abstract data type -- and so potentially, unable to fix space leaks
in them. That would make functions a kind of second class citizen: you would
always know, when choosing a representation for an ADT, that if you chose a
function then that would have an impact on space debugging later...

If one is going to provide control of evaluation order (and it IS important),
why surround it with complex rules and restrictions, when seq provides it
simply for every single expression in the language?

Space debugging is a bit of a hobby horse of mine, and I know it is considered
by many to be something of a black art. It isn't so hard really: just run the
heap profiler, find the leaks, and often the fix involves changing evaluation
order. Use seq or ~ to do so. (There can be other fixes also, but these are a
good start). I can't emphasize too much how useful the heap profiler is for
building good intuitions about how programs are evaluated. I'd strongly advise
intermediate Haskell programmers to spend some time using it: one learns a
LOT!

John Hughes