Haskell – Read and writing to file in Haskell

filehaskellio

I am trying to read the contents of a file, turn the text to upper-case and then write it back.

Here is the code I had written:

import System.IO
import Data.Char

main = do
    handle <- openFile "file.txt" ReadWriteMode
    contents <- hGetContents handle
    hClose handle
    writeFile "file.txt" (map toUpper contents)
    return ()

However, this writes nothing to the file, in fact, it even clears it.

I made some changes:

main = do
    handle <- openFile "file.txt" ReadWriteMode
    contents <- hGetContents handle
    writeFile "file.txt" (map toUpper contents)
    hClose handle
    return ()

However, I get the error resource busy (file is locked). How can I get this working and why it didn't work in both cases?

Best Answer

Lazy IO is bad, and this is generally considered to be a pain point in Haskell. Basically the contents isn't evaluated until you go to write it back to disk, at which point it can't be evaluated because the file is already closed. You can fix this in several ways, without resorting to extra libraries you can use the readFile function and then check the length before writing back out:

import Control.Monad (when)

main = do
    contents <- readFile "file.txt"
    let newContents = map toUpper contents
    when (length newContents > 0) $
        writeFile "file.txt" newContents

I would say this code is actually better anyway because you don't write back out to a file that is already empty, a pointless operation.

Another way would be to use a streaming library, pipes is a popular choice with some good tutorials and a solid mathematical foundation, and that would be my choice as well.