
Q: When trying to compose the following IO actions: readFile fileName >>= \ contents -> writeFile fileName (f contents) I get an exception: openFile: permission denied. However the following works: readFile fileName >>= \ contents -> (mapM_ putStrLn . lines) contents >> writeFile fileName (f contents) I'm assuming it has something to do with lazy IO, and the second action in the second version forces fileName to be read completely and to be closed. Why do I need to do that? I thought lazy IO was implemented in such a way that you were safe to INTERPRET the IO action as having been fully performed. (And so I would have been safe to interpret that, in the first version, in the first action, the file was released.) If that's not the case, then: (1) What is the proper way to reason about lazy IO? (2) Is there some action less arbitrary that what I cooked up in the second version that explicitly instructs to release a file or some other resource? Regards, Jacek.

I was bitten by this a couple of weeks ago.
What I was told back then boils down to the following:
- The proper way to reason about lazy IO is that nothing that doesn't need
to be
read is ever read - in much the same way as the only part of an infinite
list that
gets evaluated is the prefix that you use in your program.
- When reading and writing to the same file, it is better in general to
make the
program transactional by writing to a temp file and then renaming it to
the old file.
HTH,
Gesh
On Fri, Mar 15, 2013 at 12:52 AM, Jacek Dudek
Q: When trying to compose the following IO actions:
readFile fileName >>= \ contents -> writeFile fileName (f contents)
I get an exception: openFile: permission denied. However the following works:
readFile fileName >>= \ contents -> (mapM_ putStrLn . lines) contents >> writeFile fileName (f contents)
I'm assuming it has something to do with lazy IO, and the second action in the second version forces fileName to be read completely and to be closed.
Why do I need to do that? I thought lazy IO was implemented in such a way that you were safe to INTERPRET the IO action as having been fully performed. (And so I would have been safe to interpret that, in the first version, in the first action, the file was released.)
If that's not the case, then: (1) What is the proper way to reason about lazy IO? (2) Is there some action less arbitrary that what I cooked up in the second version that explicitly instructs to release a file or some other resource?
Regards, Jacek.
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Fri, Mar 15, 2013 at 5:52 AM, Jacek Dudek
readFile fileName >>= \ contents -> writeFile fileName (f contents)
You're reading and writing to the /same/ file back to back. With Lazy I/O. Those two just don't mix. The "permission denied" probably stems from the attempted write clashing with the previous exclusive read handle.
I thought lazy IO was implemented in such a way that you were safe to INTERPRET the IO action as having been fully performed.
Nope. The usual gotcha lying for the unwary is do h <- openFile fname1 ReadMode s <- hGetContents h hClose h writeFile fname2 s In the case fname1==fname2, a different surprise lies in store as you've just witnessed. Importing System.IO.Strict from the strict package should fix all of the above. -- Kim-Ee

That would have been useful to know three weeks ago.
Better late than never I guess.
Thanks.
On Fri, Mar 15, 2013 at 1:24 AM, Kim-Ee Yeoh
On Fri, Mar 15, 2013 at 5:52 AM, Jacek Dudek
wrote: readFile fileName >>= \ contents -> writeFile fileName (f contents)
You're reading and writing to the /same/ file back to back. With Lazy I/O. Those two just don't mix.
The "permission denied" probably stems from the attempted write clashing with the previous exclusive read handle.
I thought lazy IO was implemented in such a way that you were safe to INTERPRET the IO action as having been fully performed.
Nope.
The usual gotcha lying for the unwary is
do h <- openFile fname1 ReadMode s <- hGetContents h hClose h writeFile fname2 s
In the case fname1==fname2, a different surprise lies in store as you've just witnessed.
Importing System.IO.Strict from the strict package should fix all of the above.
-- Kim-Ee
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Fri, Mar 15, 2013 at 5:52 AM, Jacek Dudek
(1) What is the proper way to reason about lazy IO?
Lazy IO is pull-driven. The stuff that comes out of a lazy read needs to be /pulled/ by some weight to be actually read. The function putStrLn exerts weight, as you've discovered. So does writeFile. What's weightless is a pure function "lifted" (by liftM aka fmap) into IO.
(2) Is there some action less arbitrary that what I cooked up in the second version that explicitly instructs to release a file or some other resource?
Explicitly managing resources means going the whole nine yards of imperative programming. Which means reading and writing in program-specified chunks, opening and closing file handles, dealing with IO errors, etc. If this sounds a lot like manual memory management, that's because it is. So the dream is: is there an analogue of GC doing for IO what GC now does for RAM? Lazy I/O can be thought of as an hors d'oeuvre of that dream. Some have complained about a queasy feeling in the stomach afterward. Some of the strongest detractors of lazy I/O have proposed iteratees as a more realistic substitute. Just a few hours ago, Dan Doel wrote an email [1] to the haskell-cafe list explaining how some problems of Lazy I/O can be resolved by even more laziness, not less. I read his email as keeping the dream alive. (Others may differ.) GC gives the illusion of infinite RAM. What would the illusion of infinite IO be like? So to recap: if you run into issues with Lazy I/O, the fixes include: * Replace lazy with strict I/O. Make sure you have enough RAM, especially if you still use String, as opposed to ByteString. * Use strict I/O but leave off readFile. Chunk explicitly. Code imperatively. * Use iteratees * Use unsafeInterleaveIO. Watch the envelope bend. [1] http://www.haskell.org/pipermail/haskell-cafe/2013-March/107027.html -- Kim-Ee
participants (3)
-
Gesh hseG
-
Jacek Dudek
-
Kim-Ee Yeoh