Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding liftM2 in haskell

Tags:

io

haskell

I'm having a hard time understanding how liftM2 works in haskell. I wrote the following code but it doesn't output anything.

import Control.Monad
main = liftM2 (\a b -> putStrLn$show$(+) a b) readLn readLn 
like image 911
Shaurya Gupta Avatar asked Sep 03 '25 02:09

Shaurya Gupta


1 Answers

It helps to start with the type of liftM2:

liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r

The first argument is a function of 2 arguments, like (+). Normally, you would use (+) like this:

> 3 + 5
8

However, you don't have two values of type Num a => a; you are using readLn :: Read a => IO a to get values of type Num a => IO a. If you try to add those values directly:

:t (+) readLn readLn
(+) readLn readLn :: (Read a, Num (IO a)) => IO a

It requires IO a to have a Num instance. That is, you don't want to add the return values of readLn; you want to add the numbers wrapped in those return values. You could do that explicitly:

do
 x <- readLn
 y <- readLn
 return $ x + y

or, you can "modify" (+) to unwrap the arguments implicitly, then wrap the result. That's what liftM2 does: it takes a 2-argument function, and "lifts" it into the monad so it can work on wrapped values.

> :t (+)
(+) :: Num a => a -> a -> a
> :t liftM2 (+)
liftM2 (+) :: (Num r, Monad m) => m r -> m r -> m r

So while (+) 3 5 :: Num a => a, liftM2 (+) $ (return 3) (return 5) :: (Monad m, Num a) => m a. The former evaluates to 8, the later, return 8 (for whatever return does for the particular monad). Some non-IO examples:

> (liftM2 (+)) (Just 3) (Just 5)
Just 8
> (liftM2 (+)) [3] [5]
[8]
> (liftM2 (+)) (Right 3) (Right 5)
Right 8

liftM2 is very similar to map; in fact, liftM (the version that lifts 1-argument functions) is simply another name for map.

like image 61
chepner Avatar answered Sep 05 '25 21:09

chepner