I am still struggling with Haskell and now I have encountered a problem with wrapping my mind around the Input/Output monad from this example:
main = do
line <- getLine
if null line
then return ()
else do
putStrLn $ reverseWords line
main
reverseWords :: String -> String
reverseWords = unwords . map reverse . words
I understand that because functional language like Haskell cannot be based on side effects of functions, some solution had to be invented. In this case it seems that everything has to be wrapped in a do
block. I get simple examples, but in this case I really need someone's explanation:
- Why isn't it enough to use one, single
do
block for I/O actions? - Why do you have to open completely new one in if/else case?
- Also, when does the -- I don't know how to call it -- "scope" of the
do
monad ends, i.e. when can you just use standard Haskell terms/functions?