[Haskell-cafe] Automatic file closing after readFile

Jules Bean jules at jellybean.co.uk
Fri Oct 19 07:36:38 EDT 2007


I agree with Matthew's comments in the post immediately before this. It 
takes him two decent paragraphs to explain what is going on, including a 
description of WHNF, a suggestion to use pen & paper, a suggestion to 
read up on the semantics of unsafeInterleaveIO and more.

What I find inconceivable is that people can really believe that these 
unsafe-lazy functions are a sensible default IO API, given that to use 
them safely[*] you need to understand all those details. Are we claiming 
that anyone who doesn't understand the finer points of WHNF, forcing, 
and GHC's evaluation strategy should not use the haskell IO libraries?

[*] Of course, you can use them safely in certain circumstances. But 
then that doesn't scale. Because your 'first program' works, and then 
you scale up, and then it breaks, and you find yourself quite unequipped 
to work out why. Exactly the experience of the original poster in this 
thread.

readFile is actually a worse culprit than hGetContents. If you use 
hGetContents, at least you have a handle around which you can close 
explicitly with hClose, which solves the serious OS resource leak (and 
you can use a bracketing style to do it 'automatically'). Then just 
remains the asynchronous exception 'hidden bottoms' problem.

I suggest the following two versions of readFile as preferable:

readFile :: FilePath -> IO String
readFile f = B.unpack . B.readFile f
-- use strict bytestrings to read the file strictly, but into a
-- compact memory structure. Then unpack lazily into a conventional
-- string but with some luck this intermediate list might fuse away.

readFile :: FilePath -> (String -> IO a) -> IO a
readFile f act = bracket (openFile f ReadMode)
                          (hClose)
                          (hGetContents h >>= act)

-- this version still uses lazy IO inside, which I don't really like,
-- but at least it (a) doesn't open the file unless it actually gets run
-- and (b) guarantees to close it again afterwards

Jules



More information about the Haskell-Cafe mailing list