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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With