Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unlike a Functor, a Monad can change shape?

Tags:

haskell

monads

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.

For example: length $ fmap f [1,2,3] always equals 3.

With a monad, however, length $ [1,2,3] >>= g will often not equal 3. For example, if g is defined as:

g :: (Num a) => a -> [a]
g x = if x==2 then [] else [x]

then [1,2,3] >>= g is equal to [1,3].

The thing that troubles me slightly, is the type signature of g. It seems impossible to define a function which changes the shape of the input, with a generic monadic type such as:

h :: (Monad m, Num a) => a -> m a

The MonadPlus or MonadZero type classes have relevant zero elements, to use instead of [], but now we have something more than a monad.

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.

like image 606
user2023370 Avatar asked Dec 09 '11 13:12

user2023370


1 Answers

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.

like image 65
C. A. McCann Avatar answered Oct 29 '22 20:10

C. A. McCann