The core idea is that mapM
maps an "action" (ie function of type a -> m b
) over a list and gives you all the results as a m [b]
. mapM_
does the same thing, but never collects the results, returning a m ()
.
If you care about the results of your a -> m b
function (ie the b
s), use mapM
. If you only care about the effect, whatever it is, but not the resulting value, use mapM_
because it can be more efficient and, more importantly, makes your intentions clear.
You would always use mapM_
with functions of the type a -> m ()
, like print
or putStrLn
. These functions return ()
to signify that only the effect matters. If you used mapM
, you'd get a list of ()
(ie [(), (), ()]
), which would be completely useless but waste some memory. If you use mapM_
, you would just get a ()
, but it would still print everything.
On the other hand, if you do care about the returned values, use mapM
. As a hypothetical example, imagine a function fetchUrl :: Url -> IO Response
—chances are, you care about the response you get for each URL. So for this, you'd use mapM
to get a lists of responses out that you can then use in the rest of your code.
So: mapM
if you care about the results and mapM_
if you don't.
Normal map
is something different: it takes a normal function (a -> b
) instead of one using a monad (a -> m b
). This means that it cannot have any sort of effect besides returning the changed list. You would use it if you want to transform a list using a normal function. map_
doesn't exist because, since you don't have any effects, you always care about the results of using map
.
mapM_
returnsm ()
andmapM
returnsm [b]
, so if you want the results, usemapM
. If you don't want the results (e.g., you're only interested in the side effects), usemapM_
. – Mike Harris