I will have to disagree with (or at least complement) one thing Eric said: that you have to have lazy evaluation to really avoid side-effects. I would claim that not even with lazy evaluation, can you avoid side-effects completely.
The problem is when you include aspects as memory footprint and timing aspects. These things are very difficult to predict with lazy evaluation, and in many applications, controlling timing and memory
is vital. Partly for this reason, Haskell has strictness annotations to turn off lazy evaluation.
Not that this is a big problem. You can do lazy evaluation in Erlang (observe Erlang QuickCheck), but you have to be explicit about it. Conversely, you must be explicit about strict evaluation in Haskell. The notion of side-effect free programming is more fundamental, and states that you must strive to take control of side-effects and use them only where necessary.
Some side-effects are unavoidable (e.g. the processor gets warm, as Simon Peyton-Jones pointed out), but may not matter to the application; other side-effects are unwanted and perhaps disastrous (some thread frees data which is still needed by another). In complex systems, control of side-effects is essential.