0

I am writing a haskell program and am having trouble with IO types. It seems something is wrong with my readLines function and the type it is supposed to return. I want it to return a list where each element is a line from the file that is opened with the openDict function.

Here is my code.

main :: IO ()
main = do
    fileHandle <- openDict
    readLines fileHandle
    putStr "End of program\n"

readLines :: Handle -> [IO String]
readLines fileHandle
    | isEOF <- hIsEOF fileHandle = []
    | otherwise                  = [hGetLine fileHandle] ++ readLines fileHandle

openDict :: IO Handle
openDict = do
    putStr "Enter file path: "
    filePath <- getLine
    openFile filePath ReadMode

Here is the error:

Couldn't match type `[]' with `IO'
    Expected type: IO (IO String)
      Actual type: [IO String]
    In the return type of a call of `readLines'
    In a stmt of a 'do' block: readLines fileHandle
    In the expression:
      do { fileHandle <- openDict;
           readLines fileHandle;
           putStr "End of program" }

So basically, how can I store these strings from IO in a list?

2
  • 2
    [IO String] doesn't make a lot of sense here. (This is not to say it doesn't make sense at all). Try working with IO [String]. Commented Jul 27, 2014 at 5:39
  • 3
    Also | isEOF <- hIsEOF fileHandle doesn't mean what you think it means. It does not actually check for EOF, it just binds hIsEOF which you are supposed to use later. This syntax makes sense for monads like Maybe, but not for IO. See paper. Commented Jul 27, 2014 at 6:03

2 Answers 2

2

Your readLines function has several problems.

First, in this case you do not want to return a [IO String]. This would be a list of IO actions such as [print 1 >> return "a", print 2 >> return "b"] which is not what you want. You need a single IO action returning a list of Strings, so you need to use IO [String].

Second, the pattern guard isEof <- hIsEOF fileHandle is checking whether the value returned by hIsEOF (which is an IO action) is of the form isEOF, where isEOF is a variable being defined right now. Of course anything is of the form var, since a variable can be anything. So the test is always true, and not meaningful: it does not even run the IO action, for instance.

Third, the way you combine the list constructors and IO actions follows the current wrong type, so it is quite confusing.

A quite verbose way to achieve this would be instead:

readLines :: Handle -> IO [String]
readLines fileHandle = do
   eof <- hIsEOF fileHandle
   if eof then return []
          else do line <- hGetLine fileHandle
                  rest <- readLines fileHandle
                  return (line:rest)

The last lines can be shortened a bit using Applicative syntax:

import Control.Applicative
readLines2 :: Handle -> IO [String]
readLines2 fileHandle = do
   eof <- hIsEOF fileHandle
   if eof then return []
          else (:) <$> hGetLine fileHandle <*> readLines2 fileHandle   

A short version can be instead obtained by reading everything in the file and splitting into lists after that using the lines library function.

import Control.Applicative
readLines3 :: Handle -> IO [String]
readLines3 fileHandle = lines <$> hGetContents fileHandle
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for explaining the errors in my code and ways to fix them. I have decided upon the library function as chosen solution.
0

Here is the readLines that you likely really want.

readLines :: Handle -> IO [String]
readLines h = do
    isEOF <- hIsEOF h -- need to access isEOF directly, without monad, see comment about the bind
    case isEOF of
        True -> return [] -- the return adds the IO monad to the [String] type
        False -> do
            line <- hGetLine h -- need to access line separately without monad
            lines <- readLines h -- recurse down
            return $ line ++ lines -- return adds back the IO monad

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.