[Haskell-cafe] hGetContents and lazyness

Micah Cowan micah at cowan.name
Mon Sep 22 15:20:51 EDT 2008


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Max Vasin wrote:
> Hello, haskellers!
> 
> Suppose we have function (it filters package filenames from apt Packages file):
> 
>> getPackageList :: FilePath -> IO [FilePath]
>> getPackageList packageFile = withFile packageFile ReadMode $
>>                              \h -> do c <- hGetContents h
>>                                       return $ map (drop 10) $ filter (startsWith "Filename:") $ lines c -- (1)
>>     where startsWith [] _ = True
>>           startsWith _ [] = False
>>           startsWith (x:xs) (y:ys) | x == y    = startsWith xs ys
>>                                    | otherwise = False
> 
> When, I apply it to a Packages file I (surely) get an empty list. This is an expected result due to
> lazyness of hGetContents.

Combined with the fact that you're not evaluating its non-strict result
until after the file handle has been closed, yes.

Your current set of IO actions is probably similar to:
  . open file
  . process file
  . close file
  . use results from processing the file.
where the first three steps are all handled by your getPackageList. To
avoid either getting incomplete (or empty) results, or having to
strictify everything with $!, it'd be better for you to use a process
more like:
  . open file
  . process file
  . use results from processing the file.
  . close file
probably by moving the withFile outside of getPackageList, to wrap a
function that prints the results after they've been obtained. The
function passed to withFile should generally include all the processing
related to the file and its results, I believe.

> I tried changing line (1) to
> 
>> return $ map (drop 10) $ filter (startsWith "Filename:") $! lines c

The $! forces strictness, but since it's deep in the result, it isn't
evaluated until it's too late.

> Chaning it to
> 
>> return $! map (drop 10) $ filter (startsWith "Filename:") $ lines c
> 
> makes getPackageList function return several (but not all) filenames.

I think we'd need to see the actual input and expected output, to
understand what's going wrong here. It worked fine for me, for small tests.

By the way, it's good policy to always post complete, runnable examples.
Requiring anyone who wants to help you to write additional code just to
get it to run decreases the chances that someone will bother to do so.

(Disclaimer: I'm quite new to Haskell myself, so take what I say with
more than a pinch of salt.)

- --
Micah J. Cowan
Programmer, musician, typesetting enthusiast, gamer.
GNU Maintainer: wget, screen, teseq
http://micah.cowan.name/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFI1/AT7M8hyUobTrERAo5qAJ9PKLbQv09UGffmxy6/eRuGS1eYbQCgj3gH
8zvxMrGk5pvCMCOQ6LVz0Yo=
=GAvg
-----END PGP SIGNATURE-----


More information about the Haskell-Cafe mailing list