Why do countInFile1 & countInFile3 have compiler errors, when countInFile0 & countInFile2 do not. All four are the same thing.
count :: String -> String -> Int
count w = length . filter (==w) . words
present :: String -> String -> IO String
present w = return . show . count w
-- VALID: pointed readFile, regular present
countInFile0 :: String -> FilePath -> IO ()
countInFile0 w f = putStrLn =<< present w =<< readFile f
-- INVALID: pointless readFile, regular present
countInFile1 :: String -> FilePath -> IO ()
countInFile1 w = putStrLn =<< present w =<< readFile
-- VALID: pointed readFile, inline present
countInFile2 :: String -> FilePath -> IO ()
countInFile2 w f = putStrLn =<< (return . show . count w) =<< readFile f
-- INVALID: pointless readFile, inline present
countInFile3 :: String -> FilePath -> IO ()
countInFile3 w = putStrLn =<< (return . show . count w) =<< readFile
main = do
countInFile0 "bulldogs" "bulldogs.txt"
countInFile1 "bulldogs" "bulldogs.txt"
countInFile2 "bulldogs" "bulldogs.txt"
countInFile3 "bulldogs" "bulldogs.txt"
Also why does countInFile3 have this additional error that countInFile1 does not:
example_one.hs:21:27:
No instance for (Monad ((->) FilePath))
arising from a use of `=<<'
Possible fix:
add an instance declaration for (Monad ((->) FilePath))
In the expression:
putStrLn =<< (return . show . count w) =<< readFile
In an equation for `countInFile3':
countInFile3 w
= putStrLn =<< (return . show . count w) =<< readFile
With both countInFile1 and countInFile3, since you are composing three things of the form a -> IO b, you are thinking of the so-called Kleisli composition, the <=< from Control.Monad. Try
countInFile1 w = putStrLn <=< present w <=< readFile
countInFile3 w = putStrLn <=< return . show . count w <=< readFile
Or you can write countInFile3 w file = ... =<< readFile file, as you do elsewhere. readFile file (with the parameter) is an IO String, so it can be passed along by >>= or =<< to any String -> IO b. But that isn't as swank as what you intended. readFile just by itself is a FilePath -> IO String so it can be >=>'d with any String -> IO b to make a FilePath -> IO b and so on with a b -> IO c, etc. in your case ending with a FilePath -> IO ()
The second error comes from ghc trying to read =<< readFile, to do so it needs readFile to be m b for some monad m, so it settles on Monad ((->) FilePath) (this would actually make sense with Control.Monad.Instances, but would just delay getting the first error.)
If you add the file parameter to these it would be thus,
countInFile1 w file = (putStrLn <=< present w <=< readFile) file
and it is possible that you are parsing countInFile2 and countInFile0 this way, while construing =<< as <=< when actually they are like so:
countInFile0 w file = putStrLn =<< present w =<< (readFile file)
The difference is the same as that between
f n = (even . (+1) . (*3)) n
or equivalently
f = even . (+1) . (3*)
and on the other hand
f n = even $ (+1) $ 3 * n -- cp. your 0 and 2
If you delete the n from both sides here
f = even $ (+1) $ (3*) -- cp. your 1 and 3
you will get a type error akin to the ones you saw:
No instance for (Integral (a0 -> a0)) arising from a use of `even'
Where you use $ you need the parameter n -- as where you use >>= or =<< you need the parameter file. With ., as with <=<, you don't.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With