I’m just learning Haskell and I am trying to write some code that simply reads a file and creates a list of lines using the lines function. For example, I have a file called data.txt that contains the following lines:
this is line one
another line
and the final line
Here is the code I am trying to use to read this data into a list and print it to the screen:
import System.IO
import Control.Monad
main = do
let list = []
handle <- openFile "data.txt" ReadMode
contents <- hGetContents handle
let myLines = lines contents
list = listLines myLines
print list
hClose handle
listLines :: [String] -> [String]
listLines = map read
The resulting code compiles, but does not produce any output. I get the following output:
runhaskell test.hs
read_file.hs: Prelude.read: no parse
Can anyone help me to understand what is wrong with my code? Thanks.
As you might have noticed, the error message is telling you there’s problem with
read, so let’s focus on that.As I said in comments,
readtakes a string representation of a value of some data type, tries to parse it and return that value. Some examples:Some nonexamples:
What happens when you try to use
Stringversion ofread(i.e.read :: String → String)?Haskell’s representation of
String(e.g. when you evaluate something that returnsStringin GHCi) consists of series of characters enclosed in" ... "quotes. Of course, if you want to show some special character (like newline), you have to put the escaped version in there (\nin this case).Remember when I wrote that
readexpects a string representation of a value? In your case,readexpects exactly this string format. Naturally, the first thing it tries to do is to match the opening quote. Since your first line doesn’t begin with",readcomplains and crashes the program.read "hello" :: Stringfails in the same way thatread "1" :: [Int]fails;1alone cannot be parsed as list ofInts –readexpects the string to start with opening bracket[.You might have also heard of
show, which is the inverse (but in very loose sense) toread. As a rule of thumb, if you want toreada valuex, the string representationreadexpects looks likeshow x.If you were to change the content of the file to following
your code would work just fine and produce following input:
If you don’t want to change your
.txtfile, just removelist = listLines myLinesand doprint myLines. However, when you run the program, you’ll getagain. So what’s the issue?
print = putStrLn ∘ showand the default behaviour ofshowwhen it comes toshowing list of something (i.e.[a]for somea; with exception ofChar, which gets special treatment) is to produce string[ firstElement , secondElement ... lastElement ]. As you can see, if you want to avoid the[ ... ], you have to merge[String]back together.There’s nifty function called
unlines, which is the inverse oflines. Also note, thatprintcallsshowfirst, but we do not want that in this case (we got the string we wanted already)! So we useputStrLnand we’re done. Final version:We could also get rid of the unneeded
lines ~ unlinesand justputStrLn contents.