Search code examples
haskelltypespointfree

How to derive the type for `liftM2 fmap zip . mapM`?


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?


Solution

  • 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