Learn You a Haskell for Great Good! - Miran Lipovaca [148]
liftM :: (Monad m) => (a -> b) -> m a -> m b
And this is the type of fmap:
fmap :: (Functor f) => (a -> b) -> f a -> f b
If the Functor and Monad instances for a type obey the functor and monad laws, these two amount to the same thing (and all the monads that we’ve met so far obey both). This is kind of like pure and return do the same thing, but one has an Applicative class constraint, whereas the other has a Monad constraint. Let’s try out liftM:
ghci> liftM (*3) (Just 8)
Just 24
ghci> fmap (*3) (Just 8)
Just 24
ghci> runWriter $ liftM not $ Writer (True, "chickpeas")
(False,"chickpeas")
ghci> runWriter $ fmap not $ Writer (True, "chickpeas")
(False,"chickpeas")
ghci> runState (liftM (+100) pop) [1,2,3,4]
(101,[2,3,4])
ghci> runState (fmap (+100) pop) [1,2,3,4]
(101,[2,3,4])
You already know quite well how fmap works with Maybe values. And liftM does the same thing. For Writer values, the function is mapped over the first component of the tuple, which is the result. Running fmap or liftM over a stateful computation results in another stateful computation, but its eventual result is modified by the supplied function. Had we not mapped (+100) over pop before running it, it would have returned (1, [2,3,4]).
This is how liftM is implemented:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = m >>= (\x -> return (f x))
Or with do notation:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
We feed the monadic value m into the function, and then we apply the function f to its result before putting it back into a default context. Because of the monad laws, this is guaranteed not to change the context; it changes only the result that the monadic value presents.
You see that liftM is implemented without referencing the Functor type class at all. This means that we can implement fmap (or liftM—whatever you want to call it) just by using the goodies that monads offer us. Because of this, we can conclude that monads are at least as strong as functors.
The Applicative type class allows us to apply functions between values with contexts as if they were normal values, like this:
ghci> (+) <$> Just 3 <*> Just 5
Just 8
ghci> (+) <$> Just 3 <*> Nothing
Nothing
Using this applicative style makes things pretty easy. <$> is just fmap, and <*> is a function from the Applicative type class that has the following type:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
So it’s kind of like fmap, but the function itself is in a context. We need to somehow extract it from the context and map it over the f a value, and then reassemble the context. Because all functions are curried in Haskell by default, we can use the combination of <$> and <*> to apply functions that take several parameters between applicative values.
Anyway, it turns out that just like fmap, <*> can also be implemented by using only what the Monad type class gives us. The ap function is basically <*>, but with a Monad constraint instead of an Applicative one. Here’s its definition:
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap mf m = do
f <- mf
x <- m
return (f x)
mf is a monadic value whose result is a function. Because the function as well as the value is in a context, we get the function from the context and call it f, then get the value and call that x, and, finally, apply the function to the value and present that as a result. Here’s a quick demonstration:
ghci> Just (+3) <*> Just 4
Just 7
ghci> Just (+3) `ap` Just 4
Just 7
ghci> [(+1),(+2),(+3)] <*> [10,11]
[11,12,12,13,13,14]
ghci> [(+1),(+2),(+3)] `ap` [10,11]
[11,12,12,13,13,14]
Now we can see that monads are at least as strong as applicatives as well, because we can use the functions from Monad to implement the ones for Applicative. In fact, many times, when a type is found to be a monad, people first write up a Monad instance, and then make an Applicative instance by just saying