views:

159

answers:

2

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]]
+6  A: 

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]]).

sepp2k
To extend on this. In the light of the definition of mapM, apply the type of sequence:sequence :: (Monad m) => [m a] -> m [a]to the given example.mapM (\x -> [x]) [0, 1, 2] = sequence (map (\x -> [x]) [0, 1, 2]) = sequence [[0],[1],[2]] = [[0,1,2]]
Matthew S
Matthew S: Your comment was very helpful, thanks.
titaniumdecoy
+3  A: 

It's probably worth making clear that these two snippets are not 'analogous' and you shouldn't expect related results. In particular, a 'monadic' version of

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

is

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

Note the extra return.

In general, return (map f x) is the same as mapM (return . f) x.

This is because for the list monad, x >>= f 'flattens' the result of applying f to x. When you left out the return the results of applying \x -> [x] were being flattened into the result. Having an extra return cancels out the extra flattening.