I've always enjoyed the following intuitive explanation of a monad's power relative to a functor: a monad can change shape; a functor cannot.
You're missing a bit of subtlety here, by the way. For the sake of terminology, I'll divide a Functor
in the Haskell sense into three parts: The parametric component determined by the type parameter and operated on by fmap
, the unchanging parts such as the tuple constructor in State
, and the "shape" as anything else, such as choices between constructors (e.g., Nothing
vs. Just
) or parts involving other type parameters (e.g., the environment in Reader
).
A Functor
alone is limited to mapping functions over the parametric portion, of course.
A Monad
can create new "shapes" based on the values of the parametric portion, which allows much more than just changing shapes. Duplicating every element in a list or dropping the first five elements would change the shape, but filtering a list requires inspecting the elements.
This is essentially how Applicative
fits between them--it allows you to combine the shapes and parametric values of two Functors
independently, without letting the latter influence the former.
Am I correct? If so, is there a way to express this subtlety to a newcomer to Haskell. I'd like to make my beloved "monads can change shape" phrase, just a touch more honest; if need be.
Perhaps the subtlety you're looking for here is that you're not really "changing" anything. Nothing in a Monad
lets you explicitly mess with the shape. What it lets you do is create new shapes based on each parametric value, and have those new shapes recombined into a new composite shape.
Thus, you'll always be limited by the available ways to create shapes. With a completely generic Monad
all you have is return
, which by definition creates whatever shape is necessary such that (>>= return)
is the identity function. The definition of a Monad
tells you what you can do, given certain kinds of functions; it doesn't provide those functions for you.