How (fmap . fmap) typechecks

后端 未结 3 587
囚心锁ツ
囚心锁ツ 2020-12-29 06:36

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         


        
3条回答
  •  遥遥无期
    2020-12-29 06:53

    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)))
    

提交回复
热议问题