What about the performance challenges caused by lazy evaluation?
In chp 7 on understanding IO, there's also another great section that explains how IO evaluation is often confused. I share an example from the book that reads, writes, and prints files.
Memory intensive func because it attempts to read _all_ files at once because the execution of reading and writing actually only happens when the print fn is called (putStrLn files). makeAndReadFile is actually doing both reading and writing - a normal task in all other languages.
slow =
let files = mapM makeAndReadFile [1..500] :: IO [ String ]
in files >>= (putStrLn . show)
Efficent version. Here we force reads and writes to actually occur per file instead of waiting til the print. Seems like a pretty easy step to misunderstand.
safe :: IO () safe =
foldl ( \ io id -> io >> makeAndShow id ) (return ()) [1..500]* it has never been an issue for me
* I don't think the performance implications are nearly as hard to understand as often implied
* in the extreme you can just enable the STRICT language pragma in your project and forget about it :)
Really, lazy IO is a much larger source of problems, and even there, after a week or two writing IO people just learn to write code that doesn't break due to it.