[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 mailing list