Search code examples
haskelltypesfunctorapplicative

In the declaration of class Functor, can the type variables be function types?


In Haskell, the class Functor is declared as:

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

Can type variables a and b be function types, or must they be non-function types?

If they can be function types, isn't it that class Functor become effectively the same as class Applicative, in terms of making fmap able to apply to functions with arbitrary number of arguments? According to what Programming in Haskell by Hutton says:

Functors abstract the idea of fmap mapping a function over each element of a structure. Applicatives generalize this idea to allow fmap mapping functions with any number of arguments to be mapped, rather than being restricted to functions with a single argument.

In applicative:

fmap0 ::  a   ->  f   a
fmap0 =   pure
fmap1 ::  (a  ->  b)  ->  f   a   ->  f   b
fmap1 g   x   =   pure    g   <*> x
fmap2 ::  (a  ->  b   ->  c)  ->  f   a   ->  f   b   ->  f   c
fmap2 g   x   y   =   pure    g   <*> x   <*> y
fmap3 ::  (a  ->  b   ->  c   ->  d)  ->  f   a   ->  f   b   ->  f   c   ->  f   d
fmap3 g   x   y   z   =   pure    g   <*> x   <*> y   <*> z

Class Applicative is declared as:

class Functor f   =>  Applicative f   where
pure  ::  a   ->  f   a
(<*>) ::  f   (a  ->  b)  ->  f   a   ->  f   b

Thanks.


Solution

  • a and b can be function types. They can be any type. In fact, a valid Functor must allow them to be any type.

    To answer your Applicative question, let's try it.

    fmap :: (a -> b -> c) -> f a -> f (b -> c)
    

    Okay, great! Now I can take an f a and convert it to a f (b -> c). But.... then what? I can't apply f (b -> c) to an argument. It's not a function; it's a value of my functor type. If only we had a function with this signature...

    superFmap :: f (b -> c) -> f b -> f c
    

    But that sure does look a lot like

    (<*>) :: f (b -> c) -> f b -> f c
    

    which is a member of Applicative. Hence, we need Applicative to be able to apply this secondary result.

    What the other answers said is correct. We can't implement pure either, for similar reasons. But it's important to note that we can't even get (<*>) in general, because if we could then that would imply that every Functor is Apply, which is also certainly not the case.