I'm writing code that can read one or multiple files within a directory and I am having trouble having it output the contents (in my case) of both text files. My code can currently output the contents of either text file within the terminal, but not both at the same time. I also included an error message in the event that if the file or directory does not exist. I also want to modify my code so that if only one text file exists within the directory, it outputs the content of that file and provides an error message for if the second file does not exist at the same time like so:
cabal -v0 run cat test/loremm.txt test/haiku.txt (loremm.txt does not exist within the directory)
Output: cat: test/loremm.txt: No such file or directory\nHaskell's cryptic form\nis natural to some folks\nand so is haiku\n"
My Code:
module Main where
import System.Environment
import System.IO (readFile)
import System.Directory
main :: IO ()
main = do fs <- getArgs
contents <- readFiles fs
putStrLn (head contents)
readFiles :: [String] -> IO [String]
readFiles [] = return []
readFiles (x:xs) = do
doesExist <- doesFileExist x
if doesExist then do
contents <- readFile x
others <- readFiles xs
return (contents:others)
else do
others <- readFiles xs
return (("cat: " x ": No such file or directory\n") : others)
CodePudding user response:
The contents
variable in main
is a list containing a string per file, containing the contents of each. Thus, doing putStrLn (head contents)
prints only the contents of the first file. You probably meant putStrLn (concat contents)
instead.
On an unrelated note, your readFiles
method would be much simpler written in terms of traverse
, like this:
readFiles = traverse $ \x -> do
doesExist <- doesFileExist x
if doesExist then
readFile x
else
return ("cat: " x ": No such file or directory\n")
The way to notice this is that what you're doing is basically a map
, except that the result type of each thing gets wrapped in the IO
monad.