22

What is the difference between the functions liftM and mapM?

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
Luke
  • 5,771
  • 12
  • 55
  • 77

4 Answers4

36

They aren't really related. I'll try to explain what each of them does. I assume you have a basic understanding of what a monad is.

liftM :: Monad m => (a -> b) -> (m a -> m b) lets you use an ordinary function in a monad. It takes a function a -> b, and turns it into a function m a -> m b, that does exactly the same thing as the original function, but does it in a monad. The resulting function doesn't "do" anything to the monad (it can't, because the original function didn't know it was in a monad). For example:

main :: IO ()
main = do
    output <- liftM ("Hello, " ++) getLine
    putStrLn output

The function ("Hello, " ++) :: String -> String prepends "Hello, " to a string. Passing it to liftM creates a function of type IO String -> IO String -- now you have a function that works in the IO monad. It doesn't do any IO, but it can take an IO action as input, and produces an IO action as output. Therefore, I can pass getLine as input, and it will call getLine, prepend "Hello, " to the front of the result, and return that as an IO action.

mapM :: Monad m => (a -> m b) -> [a] -> m [b] is quite different; note that unlike liftM, it takes a monadic function. For example, in the IO monad, it has type (a -> IO b) -> [a] -> IO [b]. It is very much like the ordinary map function, only it applies a monadic action to a list, and produces a result list wrapped in a monadic action. For example (a pretty bad one):

main2 :: IO ()
main2 = do
    output <- mapM (putStrLn . show) [1, 2, 3]
    putStrLn (show output)

This prints:

1
2
3
[(),(),()]

What it is doing is iterating over the list, applying (putStrLn . show) to each element in the list (having the IO effect of printing out each of the numbers), and also transforming the numbers into the () value. The resulting list consists of [(), (), ()] -- the output of putStrLn.

mgiuca
  • 20,958
  • 7
  • 54
  • 70
  • 6
    Additionally, if you only care about the monadic effects of `mapM`, and not about the returned list, you can use `mapM_`. The `mapM_` function has type `Monad m => (a -> m b) -> [a] -> m ()`, it is useful in the putStrLn example, where you're probably not interested in the list of units. – Tom Lokhorst May 02 '11 at 12:19
  • @Tom Lokhorst Yep, that's right. (I couldn't come up with a useful example where the result is important on the IO monad.) – mgiuca May 02 '11 at 12:21
  • Why does mapM transform the numbers into '()'? – Luke May 02 '11 at 12:27
  • 2
    @Luke The `putStrLn :: String -> IO ()` returns a unit value (that is the `()`). Here's another example: `mapM (\x -> putStrLn (show x) >> return (x + 1)) [1, 2, 3]`. This returns a list of Ints instead of units. – Tom Lokhorst May 02 '11 at 12:38
28

First, the types differ:

liftM :: (Monad m) => (a -> b) -> m a -> m b
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

liftM lifts a function of type a -> b to a monadic counterpart. mapM applies a function which yields a monadic value to a list of values, yielding list of results embedded in the monad.

Examples:

> liftM (map toUpper) getLine
Hallo
"HALLO"

> :t mapM return "monad"
mapM return "monad" :: (Monad m) => m [Char]

... note that map and mapM differ! E.g.

> map (x -> [x+1]) [1,2,3]
[[2],[3],[4]]
> mapM (x -> [x+1]) [1,2,3]
[[2,3,4]]
phynfo
  • 4,830
  • 1
  • 25
  • 38
  • 3
    Is it `(\x -> [x + 1])` in the lambda for map? I tried the syntax without backslash for x and I get `Pattern syntax in expression context: x -> [x + 1]`. I am a Haskell newbie so I might be missing something here. – xtreak Jun 27 '16 at 15:00
11

The other answers have already explained it well, so I will just point out that you will usually see fmap used instead of liftM in real Haskell code, as fmap is just a more general version in the type class Functor. As all well-behaved Monads should be instances of Functor as well, they should be equivalent.

You may also see the operator <$> used as a synonym for fmap.

Also, mapM f = sequence . map f, so you can think of it as turning a list of values into a list of actions, and then running the actions one after another, collecting the results in a list.

hammar
  • 138,522
  • 17
  • 304
  • 385
7

liftM and mapM are quite different, as you can see via their types and their implementation:

mapM         :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f as    =  sequence (map f as)

liftM        :: (Monad m) => (a1 -> r) -> m a1 -> m r
liftM f m1   = do { x1 <- m1; return (f x1) }

so while mapM applies a monadic function to each element of a list, liftM applies a function in a monadic setting.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468