The correct thing to do here is use mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
. But if you'd like to learn a little about some useful Prelude
functions...
We have two functions here,
map :: (a -> b) -> [a] -> [b]
putStr :: String -> IO ()
if we substitute the type of putStr
into map
(ie. a ~ String
, b ~ IO ()
) we get
map putStr :: [String] -> [IO ()]
so this takes a list of strings and gives us a list of IO ()
actions; IO
computations that don't return anything usefull.
We'd like to turn that [IO ()]
into something like IO (...)
because we need IO
to be the outermost layer in order to use it in main :: IO ()
The function sequence :: Monad m => [m a] -> m [a]
from the Prelude
is exactly what we need. It takes a list of monadic actions, executes them and returns the results in a list wrapped in the monad. Intuitively it moves the monad to the outermost layer.
sequence . map putStr :: [String] -> IO [()]
This is quite close, but we still have IO [()]
instead of IO ()
, we don't really care about the [()]
result and it'd be nice to ignore it. Again, the Prelude
has what we need: sequence_ :: Monad m => [m a] -> m ()
which executes each monadic action in the list and ignores their return values.
Note that mapM_
is defined as
mapM_ f = sequence_ . map f