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.
tailwithfmap, the compiler assumes you meantfmapto refer to[]'s version offmap(fmap lines :: [String] -> [[String]]), notIO's.contentsto be in the expressionfmap waffles contents. (I wish Haskell had a decent interactive IDE so you could look at the types of variables in real time.):set -fdefer-type-errors; :set +c; :l test.hs; :type-at test.hs 2 4 2 13(that last command has format start-line, start-column, end-line, end-column) I get:: [String]. But it's super finicky, doesn't appear to coordinate well with:r(or even with later:lcommands), and having to muck about in your editor to get line and column numbers sucks. The:all-typescommand is significantly more verbose and less interactive, but ironically is more usable in some ways...<-you are using is not for nothing, it gets you inside the monad so you don't have to fmap over it yourself. Two, fromlinesyou get a[String]not aString. Three,wafflesaccepts two arguments, how exactly do you plan to use it to map over aString(orIO String, or[String], or whatever) ? Four, these arguments are numbers, you are not supposed to multiply strings.