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.
:t fmap . fmap
– Squidly:: (Functor f1, Functor f) => (a -> b) -> f (f1 a) -> f (f1 b)
– akbiggs