What does it mean to compose two Functors?
What this is talking about is the composition of type constructors like []
and Maybe
, not the composition of functions like fmap
. So for example, there are two ways of composing []
and Maybe
:
newtype ListOfMabye a = ListOfMaybe [Maybe a]
newtype MaybeOfList a = MaybeOfList (Maybe [a])
The statement that the composition of two Functors
is a Functor
means that there is a formulaic way of writing a Functor
instance for these types:
instance Functor ListOfMaybe where
fmap f (ListOfMaybe x) = ListOfMaybe (fmap (fmap f) x)
instance Functor MaybeOfList where
fmap f (MaybeOfList x) = MaybeOfList (fmap (fmap f) x)
In fact, the Haskell Platform comes with the module Data.Functor.Compose
that gives you a Compose
type that does this "for free":
import Data.Functor.Compose
newtype Compose f g a = Compose { getCompose :: f (g a) }
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose x) = Compose (fmap (fmap f) x)
Compose
is particularly useful with the GeneralizedNewtypeDeriving
extension:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype ListOfMaybe a = ListOfMaybe (Compose [] Maybe a)
-- Now we can derive Functor and Applicative instances based on those of Compose
deriving (Functor, Applicative)
Note that the composition of two Applicative
s is also an Applicative
. Therefore, since []
and Maybe
are Applicative
s, so is Compose [] Maybe
and ListOfMaybe
. Composing Applicative
s is a really neat technique that's slowly becoming more common these days, as an alternative to monad transformers for cases when you don't need the full power of monads.
A Functor
gives two mappings: one on the type level mapping types to types (this is the x
in instance Functor x where
), and one on the computation level mapping functions to functions (this is the x
in fmap = x
). You are thinking about composing the computation-level mapping, but should be thinking about composing the type-level mapping; e.g., given
newtype Compose f g x = Compose (f (g x))
can you write
instance (Functor f, Functor g) => Functor (Compose f g)
? If not, why not?