[Haskell-cafe] Explaining monads

Brian Brunswick brian.brunswick at gmail.com
Thu Aug 9 16:14:39 EDT 2007


(Better view the below in a fixed-width font!)

With all the recent monad discussions, I embarked on trying to clarify
my own thoughts about them, and started to think about things in terms
of just /where/ extra structure is 'understood'.

I think I can explain why 'a->IO b' is better than 'IO a->b'.

The full title of this is really 'Explaining monads by comparison
with comonads and arrows', but I didn't want to scare people off
without putting in the above hook!


We start with a simple single value of type 'a', and then we move into
some other category, where the objects are 'thing a' instead,
encapsulating some additional complexity - perhaps the possible
absence or multiplicity of the value, perhaps extra state, opacity,
etc.

But whatever it is, the a-ness is still key for combing these objects.
So lets look how they combine. Pay special attention to the f column.


                  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



simple application: f takes one argument, returns one result.

monad: f still takes one argument, but now understands how to put
things /into/ m. (Perhaps it just uses return::a->m a)

comonad: f has access to the entire complexity of 'w a', understands
how to get thing(s) out of it (coreturn), and distills it all down to
one simple b.

arrow: f has access to the entire complexity of the 'input' of arr b
c, and does whatever is needed, to make the complexity of the 'output'
of arr b c. Input/output may even be bidirectional!



Also we can look at the job that ??? does. Remember that ??? has
access to f as a function and can call it as many or as few times
as it wishes.

$: feeds a to f, once.

>>=: picks zero or more a's from 'm a', feeds each one separately to f.
     opens up each of the 'm b' results to combine them into one 'm b'.

=>>: feeds one or more versions of 'w a' to f, then builds a 'w b'
     in some way using the single b's (Perhaps inspired by 'w a')

>>>: links g to f in some way, so they can interact in as complex a way
     as they like. Maybe it also contributes to that complexity.



So now perhaps we can come to some conclusion about why monads are so
useful and universal as control structures. It seems that monads are
somehow the /simplest/ way of adding general control structure on top
of single values.

Notice how in a monad, the things that are combined together, the f's,
still take just one 'a' argument. So theres no extra complexity for f
in understanding its input. f can, however, put some additional
complexity into its output - it can fail, or return multiple values,
or whatever else is possible to encode in 'm a'. Still, f can be dumb,
and just pass the problem onto some other function, maybe return.

The complexity of the monad lives in one place, bind, >>=. It is bind
that has the choice of calling f multiple times if it feels like it,
and has the job of combining the results. bind is in control. f can
only give it directions by returning one 'm b' for a given a.

Contrast a comonad. Each f has to cope with an entire 'w a' on
input. Moreover, it can't communicate any of that complexity back to
cobind - it can only return one b. cobind only has the structure of 'w
a' to inspire it in how to put together 'w b', it can receive /no/
instructions from f.

Arrows are of course most general. Nothing is determined except that f
talks with g in some way, and the compatibility is named 'b'.




So given the choice of the above, which would /you/ choose, when
the nature of the problem means one-on-one application is too simple?

Surely a monad is the pick of the bunch!

* f gives merely directions on control to >>=

* f has to understand merely one single argument.
     (the monad can supply extra specialized functions like get/put,
      if we do want some complexity in there)

* All the complex control structure is handled in one place - bind.
  And it has all the information available to do it well.

Pity the poor comonad, only really suitable for infinite sequences!

Shudder at the thought of the total complexity possible using arrows,
and use them only when /really/ necessary.

monad is king!


-- 
Brian_Brunswick____brian at ithil.org____Wit____Disclaimer____!Shortsig_rules!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20070809/fd720c0a/attachment.htm


More information about the Haskell-Cafe mailing list