[Haskell-cafe] Explaining monads

Ronald Guida ronguida at mindspring.com
Fri Aug 10 23:34:35 EDT 2007


Brian Brunswick wrote:
 >                   g          f       ???          g ??? f
 >
 > application      a         a->b      flip ($)     b
 > monad bind       m a       a->m b    >>=          m b
 > comonad cobind   w a       w a->b    =>>          w b
 > arrow            arr a b   arr b c   >>>          arr a c

Kim-Ee Yeoh wrote:
 > Fmap's missing: m a, a->b, flip fmap, m b. You might want to throw in
 > Applicative there too: m a, m (a->b), flip (<*>), m b.

Here's my interpretation of the table:

----------------------------------------------------------------------
 Structure   | Subject  |  Action    |  Verb      |  Result
 ------------+----------+------------+------------+----------
 function    |  a       |  a->b      |  flip ($)  |  b
 Functor     |  f a     |  a->b      |  <$>       |  f b
 Applicative |  f a     |  f (a->b)  |  flip <*>  |  f b
 Monad       |  m a     |  a->m b    |  >>=       |  m b
 Comonad     |  w a     |  w a->b    |  =>>       |  w b
 Arrow       |  a b c   |  a c d     |  >>>       |  a b d
----------------------------------------------------------------------

Kim-Ee Yeoh wrote:
 > ... I think you'll find that each of those structures have their
 > privileged place in your code.

Agreed.  I'm still a beginner; I'm not sure how to choose one
structure over another, at least not yet.  But that's because ...

 > Monads are undoubtedly more pervasive, and that could be because there
 > aren't as many arrow and comonad tutorials, atomic ones or otherwise.

Moreover, Comonad isn't even in the standard libraries (Hoogle returns
no results for it).

When I searched for tutorials on monads, I found lots of them.  In
fact, I have heard that writing (yet another) monad tutorial is part
of standard Haskell initiation.

In my case, I have started to formulate my own idea for what a monad
tutorial should be; I might attempt to write one, too.

I read several monad tutorials, but I could not understand them at
first.  Monad transformers are even worse.  I had to sit, *think*,
draw diagrams, *think* some more, review several advanced tutorials,
and *think* even more.  I also looked up comonads, arrows, and
category theory.  Finally, monads (and monad transformers) started to
make sense for me.

I still don't understand monads just yet; but that's because my
self-test for understanding something is whether I feel I could
explain it to someone else and have it make sense.

I suppose that in order to understand monads, I need to actually *use*
some monads, and see them in action on something real (like an actual
algorithm) that I can relate to.  Learning feels like climbing a
mountain; much time and hard work is spent on the ascent, but new
understanding is achieved in the process.  Once I reach the top of the
mountain, the question is how to make the mountain easier to climb for
the next person.

My thinking is that the ultimate goal is to /invert/ the mountain,
such that the people can /ride/ the mountain (which still requires
some work) and gain new understanding in the process.  What I obtain
on the way up the mountain, future people would obtain, far more
efficiently, on the way /down/ the (inverted) mountain.

What I would like to see in a monad tutorial are some good real-use
examples that demonstrate /just/ monads.  I found it extremely helpful
to see an example of monads being used to compute probabilities and
drug test false-alarm rates.  This application seemed to used /just/
monads, without a lot of extra programming complexity on top, and it
provided a "real" example.  This is the sort of thing, combined with
the background of "what is a monad", that I think would make a really
good tutorial.

The "monadic lovers" story, in my opinion, provides an example that's
too contrived and simplistic.

On the other extreme, I found an example of arrows being used in a
digital circuit simulator.  As a tutorial on arrows, I would find this
too complex because there is too much "stuff" built up on top of the
arrows.  The concept of an arrow is lost in the complexity of
"special" classes like the ArrowCircuit class that was actually used
to simulate a circuit.  (Note: The circuit used in the example was
trivial, moreover my background is electrical engineering with a focus
on digital circuits.)

I found an example of a comonad being used to simulate a cellular
automaton; I found this example helpful, although it might be a little
too complex.

I think that what would make a truly great tutorial would be a
tutorial (or a series of tutorials) that starts with a "real" area of
exploration and proceeds to incrementally develop programs.  Each
increment would incorporate a new "bite" from the area of exploration,
which would cause an increase in complexity.  As the programs get
complicated, the monad (or comonad, or arrow) is introduced by
factoring the appropriate pattern out the code and then simplifying.

The tutorial might (1) state the monad laws and say "this is what
defines a monad", (2) say something like "let's look for this pattern
in the code" and find the pattern very easily because the example
would be designed to make the pattern fairly obvious, and (3) define a
special monad and use it to simplify the code.

If I think of something, I'll attempt to write it up and see what
happens.

Brian Brunswick wrote:
 > The full title of this is really 'Explaining monads by comparison
 > with comonads and arrows' ...

Also, arrows are supposed to be more general than both monads and
comonads.  If I could just figure out what each structure (functor,
monad, comonad, arrow) is doing, and compare and contrast them, then I
probably will have made leaps of understanding.  I have a sense that
tells me that the table above and the data structures below probably
start to scratch the surface of understanding.

----------------------------------------------------------------------
Foo       a   = Foo     { runFoo     ::            a     } -- Functor
State   s a   = State   { runState   ::     s  -> (a, s) } -- Monad
Context c a   = Context { runContext :: (a, c) ->  a     } -- Comonad
Thing   s a b = Thing   { runThing   :: (a, s) -> (b, s) } -- Arrow???
----------------------------------------------------------------------

An exceptional tutorial (it would probably have to be a series) would
use a "real" area of exploration and introduce all four structures by
incrementally developing programs (in the area of exploration) at just
the right level of program complexity for each structure to make sense
and fit into its surroundings without being either too simple or too
complicated.

If (or when) I ever make that leap of understanding, I might make an
attempt to write that tutorial.  Until then, I guess I'm still
climbing the mountain now, hoping I'll get to ride the mountain later.

-- Ron



More information about the Haskell-Cafe mailing list