I recently had the need for the following function. The idea is to zip up the original xs
values along with the mapM f xs
values.
zipMapM f xs = fmap (zip xs) (mapM f xs)
Putting it through pointfree, I got what, or me, seems like an incomprehensible yet simple result:
zipMapM = liftM2 fmap zip . mapM
So I tried to figure it out:
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
fmap :: Functor f => (a -> b) -> f a -> f b
zip :: [a] -> [b] -> [(a, b)]
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
Starting here:
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 fmap :: (Monad m, Functor f) => m (a -> b) -> m (f a) -> m (f b)
Okay so far. The next step stumped me:
liftM2 fmap zip :: Functor f => ([a] -> f [b]) -> [a] -> f [(a, b)]
How does one derive this? And then further on towards the final function?
liftM2
is doing most of the magic. In the "function Monad", ((->) a)
, liftM2
looks like this
liftM2 h f g x = h (f x) (g x)
We can use it to immediately eliminate the xs
zipMapM f xs = fmap (zip xs) (mapM f xs)
zipMapM f xs = liftM2 fmap zip (mapM f) xs
zipMapM f = liftM2 fmap zip (mapM f)
and then if we think of liftM2 fmap zip
as a function all on it's own, this is exactly the definition of (.)
(g . f) x = g (f x) -- gives us
zipMapM f = (liftM2 fmap zip . mapM) f
which we can eta reduce
zipMapM = liftM2 fmap zip . mapM