Search code examples
haskelliomonadsfile-readdo-notation

Fighting IO when reading a configuration file into Haskell


I have input data intended for my yet-to-be-written Haskell applications, which reside in a file. I don't update the file. I just need to read the file and feed it into my Haskell function which expects a list of strings. But reading the file of course yields IO data objects. I have learned that using the <- operation can "take out" somehow the strings packed in an IO structure, so I tried this attempt:

run :: [String]
run = do
  datadef_content <- readFile "play.txt" -- yields a String
  let datadef = lines datadef_content -- should be a [String]
  return datadef

I placed this into a file play.hs and loaded it from ghci by

:l play

To my surprise, I got the error message for the readFile line

 Couldn't match type ‘IO’ with ‘[]’
 Expected type: [String]
   Actual type: IO String

and for the return the error message

 Couldn't match type ‘[Char]’ with ‘Char’
 Expected type: [String]
   Actual type: [[String]]

The first seems to indicate that I couldn't get rid of the IO, and the last message seems to suggest, that lines would return a list of list of strings, which also doesn't make sense to me.

How can I do this correctly?


Solution

  • You declare run to be a [String] value. But return is not a keyword that provides the return value of a function; it is a function, with type Monad m => a -> m a. return datadef produces a value of type IO [String], which becomes the return value of the function.

    The solution is to provide the correct return type for run:

    run :: IO [String]
    run = do
        ...
    

    run can also be defined more succinctly as

    run = fmap lines (readFile "play.txt")
    

    Though the do syntax suggests is, there is no way to pull a value out of an IO action; all you can do is "push" the call to lines into the action.