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