Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"resource busy (file is locked)" error in Haskell

Tags:

haskell

I'm very new to Haskell. In fact, I'm working through this section of this tutorial. I came across this piece of code:

import System.IO     
import Data.Char  

main = do     
    contents <- readFile "girlfriend.txt"     
    writeFile "girlfriendcaps.txt" (map toUpper contents) 

Which reads the contents of the file called "girlfriend.txt" and writes the upper-cased version of the file to a new file called "girlfriendcaps.txt".

So, I wanted to modify the code a bit to take the name of the file to act on. I changed the code to this:

import System.IO
import Data.Char

main = do
    path <- getLine
    contents <- readFile path
    writeFile path (map toUpper contents)

now, obviously the major difference here is that I'm reading from and writing to the same file. As I'm thinking about it now, this must be a lazy-evaluation thing, but i'm getting the "resource busy" error message. Correct me if I'm wrong, but I guess that readFile doesn't start reading the file until writeFile asks for the contents of it. And then writeFile tries to write to the file, but it must still have the file open because it's also asking for the contents. Am I close there?

So, the real question is: how do I read from and write to the same file in Haskell? It makes sense that it's more difficult, because you will write to a different file from the file you read from more often than not, but for my own edification, how would you read and write to the same file?

like image 363
Ramy Avatar asked Feb 19 '11 19:02

Ramy


3 Answers

Indeed, this is a "lazy evaluation thing".

import System.IO
import Data.Char

main = do
    path <- getLine
    contents <- readFile path
    writeFile path (map toUpper contents)

Remember that Haskell is primarily lazy in evaluation, and so is much of the IO subsystem. So when you call 'readFile' you begin streaming data in from the file. When you then immediately call "writeFile" you start streaming bytes back to the same file

This would be an error (i.e. destroy your data), so Haskell locks the resource until it is fully evaluated, and you get a nice error message.

There are two solutions:

  • Don't destructively overwrite the file, instead, copy to a new file
  • Or, use strict IO

To use strict IO, the 'text' or 'strict' packages are recommended.

like image 181
Don Stewart Avatar answered Nov 09 '22 09:11

Don Stewart


What you're looking for is how to open a file in ReadWriteMode.

fileHandle <- openFile "fileName.txt" ReadWriteMode
contents <- hGetContents fileHandle

There's trickier stuff for navigating forwards and backwards through the file.

See Working with files and handles from RWH, and Operations on Handles at the System.IO docs.

like image 12
Dan Burton Avatar answered Nov 09 '22 07:11

Dan Burton


Depends on exactly what you are trying to do. As a rule, in any language, this is probably a bad design because if anything goes wrong either inside the program or outside (e.g user error) then you have destroyed your original data and cannot try again. It also requires that the entire file be held in memory, which is cool if its just a few bytes, but not so good when someone decides to run this on a really big file.

If you really want to do this then generate a temporary filename for the output, and then once you know that you have written to it successfully you can delete the original and rename the new one.

like image 5
Paul Johnson Avatar answered Nov 09 '22 08:11

Paul Johnson