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, read
takes a string representation of a value of some data type, tries to parse it and return that value. Some examples:
read "3.14" :: Double ≡ 3.14 :: Double
read "'a'" :: Char ≡ 'a' :: Char
read "[1,2,3]" :: [Int] ≡ [1,2,3] :: [Int]
Some nonexamples:
read "[1,2," :: [Int] ≡ error "*** Exception: Prelude.read: no parse"
read "abc" :: Int ≡ error "*** Exception: Prelude.read: no parse"
What happens when you try to use String
version of read
(i.e. read :: String → String
)?
Haskell's representation of String
(e.g. when you evaluate something that returns String
in 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 (\n
in this case).
Remember when I wrote that read
expects a string representation of a value? In your case, read
expects 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 "
, read
complains and crashes the program.
read "hello" :: String
fails in the same way that read "1" :: [Int]
fails; 1
alone cannot be parsed as list of Int
s - read
expects the string to start with opening bracket [
.
You might have also heard of show
, which is the inverse (but in very loose sense) to read
. As a rule of thumb, if you want to read
a value x
, the string representation read
expects looks like show x
.
If you were to change the content of the file to following
"this is line one"
"another line"
"and the final line"
your code would work just fine and produce following input:
["this is line one","another line","and the final line"]
If you don't want to change your .txt
file, just remove list = listLines myLines
and do print myLines
. However, when you run the program, you'll get
["this is line one","another line","and the final line"]
again. So what's the issue?
print = putStrLn ∘ show
and the default behaviour of show
when it comes to show
ing list of something (i.e. [a]
for some a
; with exception of Char
, 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 of lines
. Also note, that print
calls show
first, but we do not want that in this case (we got the string we wanted already)! So we use putStrLn
and we're done. Final version:
main = do
handle <- openFile "data.txt" ReadMode
contents <- hGetContents handle
let myLines = lines contents
putStrLn (unlines myLines)
hClose handle
We could also get rid of the unneeded lines ~ unlines
and just putStrLn contents
.