I have been going through a article(http://comonad.com/reader/2012/abstracting-with-applicatives/) and found the following snippet of code there:
newtype Com
Old question, but to me, conceptually, fmap represents "taking an a -> b and bringing it 'one level up', to f a -> f b".
So if I had an a -> b, I can fmap it to give me an f a -> f b.
If I had an f a -> f b, I can fmap it again to give me a g (f a) -> g (f a). Lift that f a -> f b function to new heights --- a new level.
So "fmapping" once lifts the function once. fmapping twice lifts that lifted function...so, a double lift.
Put in the language of haskell syntax:
f :: a -> b
fmap f :: f a -> f b
fmap (fmap f) :: g (f a) -> g (f b)
fmap (fmap (fmap f)) :: h (g (f a)) -> h (g (f b))
Notice how each successive fmap lifts the original a -> b to another new level. So,
fmap :: (a -> b) -> ( f a -> f b )
fmap . fmap :: (a -> b) -> ( g (f a) -> g (f b) )
fmap . fmap . fmap :: (a -> b) -> (h (g (f a)) -> h (g (f a)))
Any "higher order function" that returns a function of the same arity as its input can do this. Take zipWith :: (a -> b -> c) -> ([a] -> [b] -> [c]), which takes a function taking two arguments and returns a new function taking two arguments. We can chain zipWiths the same way:
f :: a -> b -> c
zipWith f :: [a] -> [b] -> [c]
zipWith (zipWith f) :: [[a]] -> [[b]] -> [[c]]
So
zipWith :: (a -> b -> c) -> ( [a] -> [b] -> [c] )
zipWith . zipWith :: (a -> b -> c) -> ([[a]] -> [[b]] -> [[c]])
liftA2 works pretty much the same way:
f :: a -> b -> c
liftA2 f :: f a -> f b -> f c
liftA2 (liftA2 f) :: g (f a) -> g (f b) -> g (f c)
One rather surprising example that is put to great use in the modern implementation of the lens library is traverse:
f :: a -> IO b
traverse f :: f a -> IO ( f b )
traverse (traverse f) :: g (f a) -> IO ( g (f b) )
traverse (traverse (traverse f)) :: h (g (f a)) -> IO (h (g (f b)))
So you can have things like:
traverse :: (a -> m b) -> ( f a -> m ( f b ))
traverse . traverse :: (a -> m b) -> (g (f a) -> m (g (f b)))