map versus mapM behavior

titaniumdecoy picture titaniumdecoy · Jun 29, 2010 · Viewed 10.5k times · Source

I'm on the I/O chapter of Real World Haskell. Monads aren't discussed in the book for another 7 chapters. Which is to say, my understanding of I/O is, at best, incomplete.

Right now I am trying to comprehend the mapM function. As I understand it, the function "executes" each element in the list which must be an "action" (IO monad).

What doesn't make sense is this example. Why does mapM return a different result than map for the same arguments?

Prelude> map (\x -> [x]) [0, 1, 2]
[[0],[1],[2]]
Prelude> mapM (\x -> [x]) [0, 1, 2]
[[0,1,2]]

Answer

sepp2k picture sepp2k · Jun 29, 2010

As I understand it, the function "executes" each element in the list which must be an "action" (IO monad).

That's true for IO, but in your code example you don't use the IO monad, you use the list monad (the function you give to mapM returns a list ([x]), not an IO).

mapM is defined as mapM f as = sequence (map f as). If f returns an IO this means that for each element in the list it constructs an IO by applying f to the element. It then turns the list of IOs that map returns into an IO "containing" a list using sequence (so when you execute the IO, you get back a list containing non-IO values).

For lists it means that it creates a list of lists by applying f to each element of as. It then uses sequence to create a list of lists which contains all possible ways to take one element of each list in the lists of lists (e.g. sequence [[1,2],[3,4]] returns [[1,3],[1,4],[2,3],[2,4]]).