[Haskell-beginners] lazy IO in readFile
Stephen Blackheath [to Haskell-Beginners]
mutilating.cauliflowers.stephen at blacksapphire.com
Fri May 7 23:41:43 EDT 2010
Andrew,
In Haskell, lazy I/O is a form of cheating, because Haskell functions
are supposed to have no side effects, and lazy I/O is a side effect. At
first, cheating seems attractive, but it takes a bit of experience to
really understand why cheating really is not a good idea, and that
Haskell is so powerful that it doesn't matter that you shouldn't cheat.
That has certainly been my experience, and I had to find out the hard
way. It sounds like you're starting to see some of the problems with
cheating.
Here's someone's philosophizing on the subject:
http://lukepalmer.wordpress.com/2009/06/04/it-is-never-safe-to-cheat/
So the short answer is, no - there is no way to force the file returned
by readFile to close.
I'd recommend using withFile and hGetLine, like this:
withFile "testfile" ReadMode $ \h -> do
...
l <- hGetLine h
If you want more speed, take a look at the stuff in Data.ByteString. If
you want proper text encoding and speed, take a look at the 'text'
package.
Steve
On 08/05/10 14:47, Andrew Sackville-West wrote:
> I'm trying to suss out the best (heh, maybe most idiomatic?) way to
> handle laziness in a particular file operation. I'm reading a file
> which contains a list of rss feed items that have been seen
> previously. I use this list to eliminate feed items I've seen before
> and thus filter a list of new items. (it's a program to email me feed
> items from a couple of low frequency feeds).
>
> So, the way I do this is to open the history file with readFile, read
> it into a list and then use that as a parameter to a filter
> function. Instead of getting confusing, here is some simple code that
> gets at the nut of the problem:
>
> import Control.Monad
>
> isNewItem :: [String] -> String -> Bool
> isNewItem [] = \_ -> True
> isNewItem ts = \x -> not (any (== x) ts)
>
> filterItems :: [String] -> [String] -> [String]
> filterItems old is = filter (isNewItem old) is
>
> getOldData :: IO [String]
> getOldData = catch (liftM lines $ readFile "testfile") (\_ -> return [])
>
> main = do
> let testData = ["a", "b", "c", "d"] :: [String]
> currItems <- getOldData
> let newItems = filterItems currItems $ testData
>
> print newItems -- this is important, it mimics another IO action I'm
> -- doing in the real code...
>
> appendFile "testfile" . unlines $ newItems
>
>
>
> Please ignore, for the moment, whatever *other* problems (idiomatic or
> just idiotic) that may exist above and focus on the IO problem.
>
> This code works fine *if* the file "testfile" has in it some subset of the
> testData list. If it has the complete set, it fails with a "resource
> busy" exception.
>
> Okay, I can more or less understand what's going on here. Each letter
> in the testData list gets compared to the contents of the file, but
> because they are *all* found, the readFile call never has to try and
> fail to read the last line of the file. Thus the file handle is kept
> open lazily waiting around not having reached EOF. Fair enough.
>
> But what is the best solution? One obvious one, and the one I'm using
> now, is to move the appendFile call into a function with guards to
> prevent trying to append an empty list to the end of the file. This
> solves the problem not by forcing the read on EOF, but by not
> bothering to open the file for appending:
>
> writeHistory [] = return ()
> writeHistory ni = appendFile "testfile" . unlines $ ni
>
> And this makes some sense. It's silly to try to write nothing to a
> file.
>
> But it also rubs me the wrong way. It's not solving the problem
> directly -- closing that file handle. So there's my question, how can
> I close that thing? Is there some way to force it? Do I need to rework
> the reading to read one line ahead of whatever I'm testing against
> (thereby forcing the read of EOF and closing the file)?
>
> thanks
>
> A
>
>
>
>
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://www.haskell.org/mailman/listinfo/beginners
More information about the Beginners
mailing list