
On Thu, Jul 12, 2007 at 09:22:09PM -0700, Michael Vanier wrote:
I stumbled across a problem with IO and strictness that I could fix, but I can't understand why the fix works. I've compressed it down into a program which simply computes the number of lines in a file. Here is a version that doesn't work:
snip snip snip
This will return a length of 0 lines for any input file. Obviously, the "let" is not being evaluated strictly (nor would we expect it to be), so that when the evaluation is requested, the file is already closed and the length of the list of lines is 0 (though I might have expected an error). I then tried this:
process_file :: FilePath -> IO () process_file filename = do h <- openFile filename ReadMode c <- hGetContents h let cs = id $! lines c -- try to strictly evaluate the let binding hClose h putStrLn $ show $ length cs
Calling hClose after hGetContents is the root of the problem, but this is a good example for understanding seq better too. To quote Chris Kuklewicz:
To quote John Meacham:
| A quick note, | x `seq` x | is always exactly equivalant to x. the reason being that your seq | would never be called to force x unless x was needed anyway. | | I only mention it because for some reason this realization did not hit | me for a long time and once it did a zen-like understanding of seq | (relative to the random placement and guessing method I had used | previously) suddenly was bestowed upon me.
I remember this anecdote because when I first read it, a zen-like understanding of seq suddenly was bestowed upon /me/. Maybe it should be in the docs. :-)
A little corrallary is (id $!) = id id $! x = x `seq` id x = x `seq` x = x = id x Remember, things defined with let don't get forced when execution in IO goes past that clause in the do block. If want to force something at a particular time between IO actions return $! should work, or Control.Exception.evaluate. I think with return $! strictness analysis might end up evaluating things earlier than you requested, but I haven't found any examples that actually get that to happen. Brandon