Finally learning how to use monads in Haskell!
I want to read a file testInput
, drop the first line, apply the function waffles
to every other line, and save the result in a file output.txt
.
I have written the following code:
main = do
contents <- tail . fmap lines . readFile $ "testInput"
result <- fmap waffles contents
writeFile "output.txt" $ concat result
waffles row col = (row - 1)*(col - 1)
Sadly the compiler complains:
waffles.hs:3:41:
Couldn't match type ‘IO String’ with ‘[String]’
Expected type: FilePath -> [String]
Actual type: FilePath -> IO String
In the second argument of ‘(.)’, namely ‘readFile’
In the second argument of ‘(.)’, namely ‘fmap lines . readFile’
waffles.hs:5:9:
Couldn't match expected type ‘[b]’ with actual type ‘IO ()’
Relevant bindings include program :: [b] (bound at waffles.hs:2:1)
In a stmt of a 'do' block: writeFile "output.txt" $ concat result
In the expression:
do { contents <- tail . fmap lines . readFile $ "testInput";
result <- fmap waffles contents;
writeFile "output.txt" $ concat result }
In an equation for ‘program’:
program
= do { contents <- tail . fmap lines . readFile $ "testInput";
result <- fmap waffles contents;
writeFile "output.txt" $ concat result }
Failed, modules loaded: none.
I find that error quite daunting. Can you help me debug it?
I also would appreciate code style advice!
EDIT: I forgot to split the lines of the file and convert them to integers. I tried solving that as follows:
main = do
contents <- tail . fmap lines . readFile $ "testInput"
contents <- fmap read . words contents
result <- fmap waffles contents
writeFile "output.txt" $ concat result
waffles row col = (row - 1)*(col - 1)
But that only introduced more confusing compiler errors.
The first line in your do
statement fails because you are trying to use tail
on an IO [String]
. You need to fmap
the tail
function:
contents <- fmap tail . fmap lines . readFile $ "testInput"
-- or....
contents <- fmap (tail . lines) . readFile $ "testInput"
Now you need a way to get every other line from contents
. You could define a simple everyOther
function for this:
everyOther :: [a] -> [a]
everyOther (x:_:xs) = x : everyOther xs
everyOther _ = []
And now you can chain that into your fmap
in the first line:
contents <- fmap (everyOther . tail . lines) . readFile $ "testInput"
Your waffles
function of (row - 1)*(col - 1)
does not seem related to what I believe the type signature should be. Try starting with a type signature and building waffles
from their. Based on your description, you are simply providing every other line to the function, so it should have signature:
waffles :: String -> String
Given that type signature for waffles
, you can apply it via:
let result = fmap waffles contents
One more thing on the output: concat
will smush all lines together. You probably want line breaks in there, so you might want to use unlines
instead.
main = do
contents <- fmap (everyOther . tail . lines) . readFile $ "testInput"
let result = fmap waffles contents
writeFile "output.txt" $ unlines result
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.