[Haskell-cafe] problem with IO, strictness, and "let"
Brandon Michael Moore
brandon at heave.ugcs.caltech.edu
Fri Jul 13 03:39:22 EDT 2007
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.
More information about the Haskell-Cafe