[Haskell-beginners] File I/O: reading from, writing to, same file in one function

Chaddaï Fouché chaddai.fouche at gmail.com
Thu Feb 5 21:30:27 UTC 2015


Hello,

On Thu, Feb 5, 2015 at 8:51 PM, Geoffrey Bays <charioteer7 at gmail.com> wrote:

> Question:
>
> As I work my way through Learn You as Haskell, I have been writing small
> File IO programs.
> Unlike the example in that book, where the author reads from one file,
> writes altered data to a temp file and then deletes the original file and
> renames the temp file, I have been trying to read from a given file, alter
> the data and then overwrite the same file with the new data. Is this
> possible?
>

It is certainly possible, whether it is a good idea is another thing
entirely.


>
> In a main  do block I have tried:
>
> contents <- readFile fileName
>
> -- do stuff to contents--
>
> writeFile fileName result
>
>
This generally won't work because readFile is lazy, it only deliver a
promise of content without reading it immediately, to allow simple
streaming. To do that it has to hold on an opened handle to fileName, it
will only close it when the the EOF is reached in contents, so when
contents is completely evaluated, which depending on your code will
probably only happen when result is completely evaluated... Except it won't
be since writeFile would be the one doing this evaluation and writeFile
won't even start because it needs to open fileName in WriteMode and
fileName is still open in ReadMode...


> After using appendFile to add items to the file, when I call the function
> to read and write I get this error:
> openFile: resource busy, (file is locked)
>
> I have also tried using the file handles like so:
>
>     handle <- openFile fileName ReadWriteMode
>     contents <- hGetContents handle
>  -- do stuff to contents --
>
>     hPutStr handle  results
>    hClose handle
>

Interleaving lazy IO in the form of hGetContents and trying to write on the
same handle strike me as a really bad idea... Supposing it worked, where
exactly in the file did you think this would write ? ReadWriteMode is
generally a very bad idea (whatever the language) except if you're
manipulating a binary format with fixed length fields and, even then, it
requires good discipline.


>
> I have also tried opening with one handle to read, closing it, then using
> another handle for writing,
> but the error message is that the handle to the file is closed.
>


That should work, provided you do it properly but you probably still used
lazy IO so you're left with a promise of content but you closed the handle
that was supposed to provide this content... If you were to use strict IO
though that would mean you would have to get the entire content of your
file in memory which depending on the size of this file may be bad or even
catastrophic (hint : String is *extremely* wasteful, Text is better for
text and ByteString for binary formats).


>
> I am thinking that the the author of LYAH maybe has good reasons to use a
> temp file, or is there another way that is not too difficult to grasp??
>
> Right !
Reading from a file and writing to a temp file then closing everything and
renaming *is the proper way* to handle your workflow, it has only
advantages :

   - you can stream the content of your file while modifying it, using far
   less memory (RAM) in exchange for an insignificant bump in disk usage,
   - your modification is atomic (in a proper filesystem) : if your program
   is interrupted for whatever reason, you're not left with a corrupted file
   but the intact original and an incomplete temp file you can use for
   diagnostic

This is actually true whatever language you're using (and is the
recommended way by all experts) ! But in Haskell the other way is even
trickier to get right if you don't understand when you should use strict IO
(tip : getContents and readFile are both lazy IO and completely
inappropriate if controlling *when* the IO happens is important, like here).

So given that the other way is both harder to get right and generally a bad
idea anyway, I really would recommend to use the proper way.


-- 

Jedaï
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/beginners/attachments/20150205/a0470df0/attachment.html>


More information about the Beginners mailing list