I understand a little bit about IO. I understand that you can use readFile
to get the content of a file. For example like this:
main = do
let inputFilePath = "C:\\Haskell\\myawesomeprogram\\files\\a.txt"
content <- readFile inputFilePath
print content
Calling the program:
> runghc myawesomeprogram
"AAA"
Awesome, that works! Now I want to read the content from multiple files. I tried something like this:
files = ["C:\\Haskell\\myawesomeprogram\\files\\a.txt", "C:\\Haskell\\myawesomeprogram\\files\\b.txt","C:\\Haskell\\myawesomeprogram\\files\\c.txt"]
main :: IO ()
main = do
filesContent <- readFiles files
print filesContent
readFiles (x:xs) = do
content <- readFile x
content : readFiles xs
This will give me the following error message:
myawesomeprogram.hs:6:21: error:
* Couldn't match type `[]' with `IO'
Expected type: IO String
Actual type: [String]
* In a stmt of a 'do' block: filesContent <- readFiles files
In the expression:
do filesContent <- readFiles files
print filesContent
In an equation for `main':
main
= do filesContent <- readFiles files
print filesContent
|
6 | filesContent <- readFiles files
| ^^^^^^^^^^^^^^^
myawesomeprogram.hs:9:1: error:
Couldn't match type `IO' with `[]'
Expected type: [FilePath] -> [String]
Actual type: [FilePath] -> IO String
|
9 | readFiles (x:xs) = do
| ^^^^^^^^^^^^^^^^^^^^^^^...
myawesomeprogram.hs:11:5: error:
* Couldn't match type `[]' with `IO'
Expected type: IO String
Actual type: [String]
* In a stmt of a 'do' block: content : readFiles xs
In the expression:
do content <- readFile x
content : readFiles xs
In an equation for `readFiles':
readFiles (x : xs)
= do content <- readFile x
content : readFiles xs
|
11 | content : readFiles xs
| ^^^^^^^^^^^^^^^^^^^^^^
I'm doing something wrong, however, I can't see a way to do it right. Can you do it the right way?
CodePudding user response:
readFiles xs
is not a list, so you can't prepend an item to it. Instead, it's an action, which, when executed, would produce a list.
More specifically, the type of readFiles xs
is IO [String]
(IO
is the type of executable actions), whereas a list would have type [String]
. This is what the error message is telling you: cannot match type IO [String]
with [String]
.
So to get the list, you have to execute the action, just like you're executing readFile
readFiles (x:xs) = do
content <- readFile x
theRest <- readFiles xs
pure (content : theRest)
Also note that readFiles
doesn't know what to do when its argument is an empty list. You should get a warning about it at compile time, and if you don't fix it, you'll get a crash at runtime.
To fix, just add an equation for the empty list case:
readFiles [] = pure []
readFiles (x:xs) = do
content <- readFile x
theRest <- readFiles xs
pure (content : theRest)