Search code examples
haskelltypestype-inferencefunction-compositionpointfree

How does `<$` function work in the Functor class?


In the Functor class definition we have the <$ function defined as:

class  Functor f  where
    fmap        :: (a -> b) -> f a -> f b

    (<$)        :: a -> f b -> f a
    (<$)        =  fmap . const

The const function has the definition:

const                   :: a -> b -> a
const x _               =  x

I know that the <$ function is equivalent to:

\x -> fmap (const x)

How does fmap . const equate to the lambda expression above? My understanding of function composition is that output type of const should match the input type of fmap, but the input type of fmap is the function (a -> b) not a which is what the const function outputs.


Solution

  • The other answer does a good job of addressing the question, "How does fmap . const equate to the lambda expression above?", so I want to address a different part:

    My understanding of function composition is that output type of const should match the input type of fmap, but the input type of fmap is the function (a -> b) not a which is what the const function outputs.

    In this answer, I will argue that the output type of const is indeed a function as needed by fmap.

    Let's rewrite the types of fmap and const, using separate type variables in each to avoid confusion:

    fmap :: Functor f => (a -> b) -> (f a -> f b)
    const :: c -> (d -> c)
    

    Now, one must ask: what is the output type of const? In your question, you posit that the output type is c (after correcting for type variable renaming as above). But in fact this is a slight misunderstanding; the true output type is d -> c!

    The output of const is actually a function. Now, as you say, its output must match the input of fmap. With our naming above, this means we must choose to satisfy the equation d ~ a (read: type d and type a are the same type) and satisfy c ~ b. Then we will have:

    const        :: b -> (a -> b)
    fmap         ::      (a -> b) -> (f a -> f b)
    fmap . const :: b             -> (f a -> f b)