Haskell map mapM mapM_ example

The Control.Monad module contains a series of functions like mapM, mapM_, how and when to use them, what's the difference between map and those functions?

The two functions are designed to complement the limitations of map. Suppose we have a function that will accept a string, the function will read a line from input and prefix that string to the parameter and then put the concatenated string to output.

(\line -> (getLine >>= (\x -> putStr $ (x ++ line ++ " \n"))))

What happens if we map that function to a list of string?

map (\line -> (getLine >>= (\x -> putStr $ (x ++ line ++ " \n")))) ["world","sekai"]

We get an error

<interactive>:51:1: error:
    ? No instance for (Show (IO ())) arising from a use ofprint? In a stmt of an interactive GHCi command: print it

Let's see the type of it

Prelude Control.Monad.Reader System.IO.Unsafe> :t map (\line -> (getLine >>= (\x -> putStr $ (x ++ line ++ " \n")))) ["world","sekai"]
map (\line -> (getLine >>= (\x -> putStr $ (x ++ line ++ " \n")))) ["world","sekai"]
  :: [IO ()]

It tells us what the ghci get is a list of IO actions, the ghci don't know how to handle it. To handle it in the ghci we must extract the IO action from the list one by one and perform it.

let foo = map (\line -> (getLine >>= (\x -> putStr $ (x ++ line ++ " \n")))) ["world","sekai"]
foo !! 0
foo !! 1
unsafePerformIO $ foo !! 0
unsafePerformIO $ foo !! 1

Now change the map to mapM. Evaluate it in repl will trigger the IO action one by one the type of the mapM version is IO [()]. You can read it as IO of list of actions, the map version is a list of IO actions, see the difference?

The definition of mapM is mapM f list = sequence (map f list), the code below shows how sequence works

Prelude Control.Monad.Reader System.IO.Unsafe> sequence [putStr "foo", putStr "bar"]
Prelude Control.Monad.Reader System.IO.Unsafe> :t sequence [putStr "foo", putStr "bar"]
sequence [putStr "foo", putStr "bar"] :: IO [()]
Prelude Control.Monad.Reader System.IO.Unsafe> :t [putStr "foo", putStr "bar"]
[putStr "foo", putStr "bar"] :: [IO ()]

The mapM still return the result of IO action, in this case, the action is to print to output and the effect already happened, the results are not relevant anymore. You can use mapM_, the return type is IO (), it means we only care about performng the effects, the definition of mapM_ is mapM_ f list = sequence_ (map f list).