Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: read a file by line

I recently did the Waterloo CCC and I feel that Haskell is the perfect language for answering these types of questions. I am still learning it. I am struggling a bit with the input, though.

Here's what I'm using:

import IO
import System.Environment
import System.FilePath

…

main = do
    name <- getProgName
    args <- getArgs
    input <- readFile $
        if not (null args)
            then head args
            else dropExtension name ++ ".in"
    let (k:code:_) = lines input
    putStrLn $ decode (read k) code

As you can see, I'm reading from the command-line given file path or from j1.in for example, if this program is called j1.hs and compiled to j1.

I am only interested in the first two lines of the file, so I have used pattern matching to get those lines and bind them to k and code, in this example. And I then read k as an integer and pass it and the code string to my decode function, which I output.

I'm wondering if readFile is loading the entire file into memory, which would be bad. But then I started thinking, maybe since Haskell is lazy, it only ever reads the first two lines because that's all it's asked for later on. Am I right?

Also, if there is anything with that code sample that could be better or more idiomatic, please let me know.

like image 761
mk12 Avatar asked Mar 02 '12 00:03

mk12


2 Answers

The documentation for readFile says:

The readFile function reads a file and returns the contents of the file as a string. The file is read lazily, on demand, as with getContents.

So yes, it will only necessarily read the first two lines of the file (buffering means it will probably read more behind the scenes). But this is a property of readFile specifically, not of all Haskell I/O functions in general.

Lazy I/O is a bad idea for I/O-heavy programs (e.g. webservers) but it works nicely for simple programs that don't do much I/O.

like image 103
dave4420 Avatar answered Sep 28 '22 08:09

dave4420


Yes, readFile is lazy. If you want to be explicit about it, you could use:

import Control.Monad (replicateM)
import System.IO

readLines n f = withFile f ReadMode $ replicateM n . hGetLine

-- in main
    (k:code:_) <- readLines 2 filename

This will ensure the file is closed as soon as possible.

But the way you've done it is fine.

like image 21
porges Avatar answered Sep 28 '22 09:09

porges