Functors, Applicative Functors, and Monads
Functor
and Monad
both describe operations for types that are still waiting for a type argument.
One way to understand them is that Functor
describes containers in which the contained data can be transformed, and Monad
describes an encoding of programs with side effects.
This understanding is incomplete, however.
After all, Option
has instances for both Functor
and Monad
, and simultaneously represents an optional value and a computation that might fail to return a value.
From the perspective of data structures, Option
is a bit like a nullable type or like a list that can contain at most one entry.
From the perspective of control structures, Option
represents a computation that might terminate early without a result.
Typically, programs that use the Functor
instance are easiest to think of as using Option
as a data structure, while programs that use the Monad
instance are easiest to think of as using Option
to allow early failure, but learning to use both of these perspectives fluently is an important part of becoming proficient at functional programming.
There is a deeper relationship between functors and monads.
It turns out that every monad is a functor.
Another way to say this is that the monad abstraction is more powerful than the functor abstraction, because not every functor is a monad.
Furthermore, there is an additional intermediate abstraction, called applicative functors, that has enough power to write many interesting programs and yet permits libraries that cannot use the Monad
interface.
The type class Applicative
provides the overloadable operations of applicative functors.
Every monad is an applicative functor, and every applicative functor is a functor, but the converses do not hold.