mk12
mk12

Reputation: 26434

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.

Upvotes: 6

Views: 8477

Answers (4)

dave4420
dave4420

Reputation: 47062

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.

Upvotes: 9

MathematicalOrchid
MathematicalOrchid

Reputation: 62848

I/O in Haskell isn't usually lazy. However, the readFile function specifically is lazy.

Others have said the same thing. What I haven't seen anybody point out yet is that the file you've opened won't get closed until either the program ends or the garbage collector runs. That just means that the OS file handle might be kept open longer than necessary. In your program that's probably no big deal. But in a more complicated project, it could be.

Upvotes: 3

Daniel Fischer
Daniel Fischer

Reputation: 183978

readFile reads the file lazily, so it won't read the entire file into memory unless you use the entire file. It will not usually read exactly the first two lines, since it reads in blocks, but it will only read as many blocks as needed to find the second newline.

Upvotes: 3

porges
porges

Reputation: 30590

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.

Upvotes: 9

Related Questions