Frege basics: File I/O

Intro

All examples are based on IO.fr module functions. You can find them Here.

Reading

We’ll be using the openReader function. It creates a BufferedReader from a given file path in the form of a String.

openReader :: String -> IO BufferedReader

The first example reads a file getting a list of lines out of it:

readLines :: String -> IO [String]
readLines path = do
  reader   <- openReader path
  reader.getLines

Here you have a shorter version:

readLines' :: String -> IO [String]
readLines' path = openReader path >>= _.getLines
Here we were using the placeholder notation _.XXX to avoid creating intermediate values.

But bear in mind that if you give a wrong path the function call will raise an error. Here is an example returning an empty list in case the file doesn’t exists.

safeReadLines :: String -> IO [String]
safeReadLines path = openReader path >>= _.getLines
    `catch` (\(e :: FileNotFoundException) -> return [])

Writing

As opposed to openReader there is a openWriter to write to a file. The openWriter and appendWriter functions return a PrintWriter instance. Lets see how can we use them. In the first example I’m setting the content of a file.

writeStringToFile :: String -> String -> IO ()
writeStringToFile path line = do
    writer   <- openWriter path (1)
    writer.print line (2)
    writer.flush (3)
1 Getting the PrintWriter instance
2 Writing to file
3 Flushing to reflect the changes
flushing is important when writing to a file.

But most of the time we would like to append information to a file. The following example does exactly that.

appendLine :: String -> String -> IO ()
appendLine path line = do
    writer   <- appendWriter path (1)
    writer.println line (2)
    writer.flush (3)
1 Getting the PrintWriter instance
2 Appending content to a file
3 Flushing to reflect the changes