
Functors, applicatives, and monads in pictures - tellarin
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
======
DanWaterworth
I think that many monad tutorials, including this one, fall into the same
trap. They explain monads without giving any motivation, or when they do, it
seems like monads only exist to get around problems caused by being pure.

When you program in normal languages, you have the ability to perform effects
all of the time. You can always read/write to/from files, you can always raise
exceptions or otherwise change the control flow. There's nothing you can't do.

Haskell is different. In Haskell, by default, you don't have any effects at
your disposal at all. You can create values from other values, that's it. When
you want to program with effects, you use a monad. The type of monad that you
use decides the effects that you are allowed. For example, the maybe monad
instance gives you the "fail without an error effect". The either monad gives
you the "fail with an error effect". The list monad instance gives you the
non-determinism effect. etc. You might expect me to say that IO gives you the
"interact with the outside world effect", but that's not really true, forget
what you know about IO for the moment.

The continuation monad instance is a bit special. In a sense, it is the most
general of the monad types, because every monad instance can be seen as a
specialization of it. The continuation monad allows every kind of effect. At
this point, you might be thinking, "but how can this be, it doesn't allow IO".

To reconcile these facts. I present the coroutine monad. It has an effect,
yield, that gives a value to the caller and waits for the caller to respond
with a value so that it can continue. Here are some types:

    
    
        runCoroutine :: Coroutine i o x -> Either (o, i -> Coroutine i o x) x
        yield :: o -> Coroutine i o i
    

So running a coroutine either produces a value of o and function to resume the
coroutine or it completes with a value of x. IO is very similar to the
coroutine monad. When a Haskell program gets to a function that talks to the
outside world it yields control to the runtime environment, the runtime
environment does the actual IO and passes the result back so that the program
can continue.

This is why Haskell is pure despite having effects. Haskell programs don't do
IO, they yield control to the runtime environment whenever IO needs to be
done.

So why are monads useful? They allow you to restrict the effects that are
possible in different parts of your programs. This aids in reasoning about
your programs, because you don't have to think about whole classes of
behaviour, but they're less useful in other languages where you already have
effects everywhere.

~~~
zohebv
I don't know how else to put it, but at least half of what you have written
above is completely wrong.

> but they're less useful in other languages where you already have effects
> everywhere.

Scala has side effects everywhere, yet the Monad is extensively used in Scala
programs. Monads have nothing to do with "effects".

Monads are not used to decide the kind of "effects" you are allowed. By
calling every Monad a kind of "effect", you are just begging the question.
Monads are merely objects that follow 3 monad laws, no more no less.

> This is why Haskell is pure despite having effects. Haskell programs don't
> do IO, they yield control to the runtime environment whenever IO needs to be
> done.

How is this different from a C program? Haskell is pure in the presence of IO,
because the type system tracks IO calls and forces any function that even
indirectly refers to a quantity obtained via IO to modify its type signature
to reflect the IO action. This can be done with/without Monads. Haskell IO is
a Monad, because it pretty much analogous to the State Monad and Haskell has
special Monad syntax that lets you pass the World State implicitly and write
imperative code in an imperative style.

~~~
DanWaterworth
> at least half of what you have written above is completely wrong.

That's quite an assertion.

> Scala has side effects everywhere, yet the Monad is extensively used in
> Scala programs. Monads have nothing to do with "effects".

I didn't say monads weren't used outside of Haskell, I say that they are less
useful. I haven't programmed in Scala before, I assume monads are used for
their notation. They aren't required.

> Monads are not used to decide the kind of "effects" you are allowed. By
> calling every Monad a kind of "effect", you are just begging the question.
> Monads are merely objects that follow 3 monad laws, no more no less.

I don't know if you're familiar with group theory, but in group theory, every
finite group is a specialization of the group of permutations. Groups are
defined abstractly by the operations that they allow in the same way that
monads are, but this definition for finite groups is as general as saying that
every group is a subgroup of the group of permutations.

It's the same for monads, they are defined abstractly, but the abstract
definition is equivalent to saying monads are all of the things that you can
create by a specific kind of specialization of the continuation monad.

Continuations are an effect. Specializations of continuations are also
effects. Monads define effects. What's really interesting is thinking about
monads in terms of the Curry-Howard Isomorphism, on one end of the scale, the
identity monad, you have constructive logic and at the other end,
continuations, you have classical logic, what's in the middle? Does the list
monad instance have an interesting associated logic?

Which question am I begging?

> How is this different from a C program?

It's just a different way of looking at programs. The distinction is never
drawn for C programs.

> Haskell is pure in the presence of IO, because the type system tracks IO
> calls and forces any function that even indirectly refers to a quantity
> obtained via IO to modify its type signature to reflect the IO action.

This is quite a shallow way of looking at it.

> This can be done with/without Monads.

Sure, you could use continuations or uniqueness types.

> Haskell IO is a Monad, because it pretty much analogous to the State Monad

AFAIK, that's not defined in the Haskell standard, but that is the way that
GHC does it.

> Haskell has special Monad syntax that lets you pass the World State
> implicitly and write imperative code in an imperative style.

No, it's not syntax that's letting you pass the world state implicitly. In
GHC:

    
    
        IO x = State RealWorld x = (RealWorld -> (RealWorld, x))
    

You can't get at this function and RealWorld doesn't have any constructors, so
you can't call an IO operation or do other weird things. Anyway, it doesn't
really matter how IO is actually defined. What matters is it's interface.

~~~
zohebv
Reading your response, I think I need to read more on the topic. I haven't
read Moggi's paper and I need to look up on the continuation Monad. You are
probably correct and I am probably wrong. However, I will let my original post
remain as a matter of historical record :-)

------
jerf
This has one of the usual dangers of trying to do a visualization of a monad,
which is that it strongly implies that there is a one-to-one relationship
between what you put in and what comes out, which is basically saying that the
bind function of the monadic interface implementation will only call the
function it receives once. This is not true; it may be called zero times
(Nothing, empty list), once (Just something, a list of one element), or many
times (once for each element in the list for the list monad, for instance),
which is a critical element to understanding the monad interface. So, you
know, be aware of that I guess.

~~~
egonschiele
/r/haskell has already discussed this at length:
[http://www.reddit.com/r/haskell/comments/1co0s5/functors_app...](http://www.reddit.com/r/haskell/comments/1co0s5/functors_applicatives_and_monads_in_pictures/)

And I've given Maybe's Monad instance definition as well as explained it in
pictures. If there's something missing please let me know specifically.

------
balloot
Yikes. That might as well be titled "Why functional languages will never catch
on." It took something dead simple (a function that adds 3), and made it
insanely complicated. Why would anyone _choose_ to deal with that?

~~~
seldo
They chose a deliberately over-simplified example in order to demonstrate the
core concepts; I don't begrudge them that (nobody needs a program that prints
out "Hello World!").

The audience level of this piece is confusing -- it _looks_ like a friendly,
cartoony introduction for a general audience, but it actually assumes you
already know Haskell.

Basically, you and I are not the intended audience.

~~~
balloot
I know a bit about Haskell and have played with it. I guess it just seems like
functional languages are for those who want to solve super complicated mental
exercises when doing tasks that would otherwise be easy using a traditional
language.

There are very few things in the world of CS/programming where I can't follow
along and grok what's going on, especially when laid out in a tutorial form
like this. The fact that I was totally lost when reading this is not a good
sign when you're trying to get people to adopt your school of thought.

~~~
chongli
>There are very few things in the world of CS/programming where I can't follow
along and grok what's going on, especially when laid out in a tutorial form
like this.

That's probably because most of the tutorials you've followed have adopted an
imperative style of programming. It is easy for you to follow along because it
really isn't all that different from what you are accustomed to. Functional
programming really is a different beast entirely. It's often said (though I
remain skeptical) that it's actually easier to learn functional programming as
a non-programmer than to _un-learn_ your imperative programming tendencies.

>The fact that I was totally lost when reading this is not a good sign when
you're trying to get people to adopt your school of thought.

I don't think that's a fair assessment. You could say the same thing about
starting from functional programming and switching to imperative; some people
actually do this! They find the idea of mutating a variable to be completely
counter-intuitive!

~~~
balloot
I would love to meet this mythical being who understands functional
programming but has no clue how to write an imperative program.

~~~
wwweston
While going through a Math undergrad program in the mid-90s, I noticed that
some number of my fellow students had a distaste for programming.

As someone who'd done reasonably well with both and had thought of them as
heavily overlapping areas, I thought this was interesting, so I tried to poke
at this a bit.

One of the concepts I got back from a talented classmate was basically a
complaint about mutable variables -- that in an algebraic description of
relationships for a given system what we call "variables" are less variable
and more "unknowns" which represent _fixed_ if unidentified quantities. Others
similarly noted there was something absurd about writing out equations like "x
= x + 2".

~~~
tripzilch
I can see why they think that, but the important thing is that "x = x + 2" is
not an equation. It's just an arguably poorly chosen operator (Pascal gets
this right and uses ":=" for assignment).

The trick is to stop reading that line (in your head) as "x _is equal to_ x
plus two", but instead "x _is assigned the value of_ x plus two".

~~~
wwweston
> It's just an arguably poorly chosen operator (Pascal gets this right and
> uses ":=" for assignment).

That's one potential takeaway, but when I turned over the complaint in my
mind, this was arguably another way of complaining about mutability.

Also, given the timing, it's pretty likely that one of the languages they'd
learned was Pascal: that was the language of the first few CS classes at my
school in the 90s, it was also the language of any high school curriculum that
led to the AP exam, and Borland's Pascal products were still pretty popular PC
dev environments.

They could've also been unlucky and received their first exposure by choosing
to do a numerical analysis class in Fortran or C, of course. :/

------
seldo
I reluctantly confess that, knowing nothing about Haskell, I was lost by the
4th diagram. Presumably if you know Haskell, the line "data Maybe a = Nothing
| Just a" makes sense, but I can't parse it.

~~~
ominous_prime
I don't know Haskell, but it seems pretty clear: "something called a Maybe
named 'a'; is equal to Nothing, or just 'a' itself" (hmm, I'm not sure that's
a whole lot clearer :-/ )

~~~
shoo
The variable 'a' there is a placeholder for a type.

e.g. loosely translating the idea of Maybe to C++ syntax (perhaps more
familiar?) might give something resembling:

    
    
        Maybe<int> x = Just<int>(30);
        Maybe<int> y = Nothing<int>();
    

x and y are values of type Maybe<int>. I.e. we've plugged in the type int for
the type variable a.

Given an arbitrary value of type Maybe<int> you have two cases to consider -
either it is a "Nothing" which contains no further data, or it is a
"Just<int>" which contains an int value.

You can think of the type Maybe<int> as being a "nullable int" type or perhaps
a special collection of ints that can only contain 0 or 1 int value.

(beware: my knowledge of both haskell & c++ is not fantastic)

------
GhotiFish

       > (+) <$> (Just 5)
       Just (+5)
       > Just (+5) <$> (Just 4)
       ERROR ??? WHAT DOES THIS EVEN MEAN WHY IS THE FUNCTION 
         WRAPPED IN A JUST
    

A functor takes a function, and applies it to the inside of a context, but all
of a sudden now a functor can't take a function and apply it to the inside of
a context.

No, that will never make sense. Sorry. Haskell is hard.

edit: oh, it's in a just value, I didn't notice that the first time I read it,
for... some reason.

~~~
antninja
"Just (+5)" is not a function. This would have worked with:

(+5) <$> (Just 4)

~~~
tel
Or Just (+5) <*> Just 4 as it turns out.

------
virtualwhys
"Bill O’Reilly being totally ignorant about the Maybe functor" and the
corresponding pic was (for me) LOL funny ;-)

My intro to functional programming has been via Scala; not quite Haskell, but
makes use of some of the same functional idioms, so a good place to get your
feet wet in FP if you're tied to the JVM (yes, yes, there's also Clojure,
prefer Scala's type system and easily grok-able syntax).

------
babesh
Still have not encountered a good explanation of monads although there seems
to be a new try on Hacker News every couple of weeks. Wonder if/when it will
reach mainstream and become part of the common vocabulary of programming.
Wonder if a limited syntax would help.

Maybe a better example than the common 'or nothing' would help in this case.

~~~
lbrandy
I just worked my way through learning-myself-a-haskell yesterday, so this is
timely. And I agree with your criticism. The `Maybe` example is simple, and
helps with drawings, but it really lacks motivation. I understand all the
words, and all the definitions, but it doesn't really explain what problem
we've solved.

The payoff of the article is, I think, supposed to be:

getLine >>= readFile >>= putStrLn

Which looks great. But what I'd like to see is a longer explanation of why
this is better than the alternatives (like simple function composition).

~~~
ufo
I think the biggest problem with monad tutorials is that it seems that
everyone finally "gets" monads in a different way so the monad tutorials that
get written lack motivation. For example, I finally "got" monads when I was
working with a promise library in Javascript...

That said, one thing I noticed is that there are two main "types" of monad
tutorials. The first one is the the one taken by the OP and I call it the
"category theory" approach. It tends to show what sorts of things you can do
with monads and how they work mathematically but they kind of lack an
explanation for why you should bother with monads in the first place.

The other approach for monads is the "monads as an useful interface" approach.
One paper I would highly recommend in this category is the "awkward squad"
paper by Simon Peyton Jones:

[https://research.microsoft.com/en-
us/um/people/simonpj/Paper...](https://research.microsoft.com/en-
us/um/people/simonpj/Papers/marktoberdorf/)

Its an introduction to monads that helped me a lot and it also explains quite
a bit about the history of monads.

Which lets me come back to your main point:

> why this is better than the alternatives (like simple function composition)

Because simple function composition doesn't work! While nowadays people like
to point out that Haskell is a pure functional language, noone would have
bothered with making a pure language (back then) if all you gained was all
this headache with monads. The reason Haskell was pure in the first place was
because its original goal was to be a lazily-evaluated language and that
lazyness kind of forces you into pureness. You might think the order of
evaluation of function arguments in C being unspecified is bad, but Haskell
would be even worse since the order of evaluation for _everything_ is
unspecified and some things might not even end up being evaluated at all!

They ended up trying all sorts of approaches for adding effectful computation
to Haskell and the most successful one was the monad one (the SPJ paper goes
over this a bit). If you pay attention, the monad interface forces
computations to be single threaded (you can't "branch out" monadic effects)
and doesn't let you generally start or end an effectful computation wherever
you want, meaning that whoever defined the monad you are using can control how
effects are started or terminated via what functions they expose in the public
interface (Maybe exposes the constructors directly so you can pattern match on
them but IO makes it so `main` the only place you can start an IO computation)

Finally, this gets us back to notational problems. While monads make things
really neat in the type level and as a platform for effects, it can end up
really noisy in practice:

    
    
        getNumber >>= (\a -> getNumber >>= (\b -> f a b)))
    

So for monads we have "do" notation that lets you write things in a neater
way:

    
    
        a <- getNumber
        b <- getNumber
        f a b
    

This is really helpful and 99% of the time, do notation is precisely why
people bother making things into monads instead of defining their own versions
of (>>=), like they do in Javascript with promises. However, writing all those
intermediate variables can be annoying and doesn't feel "composeable". The
Functor and Applicative type classes provide methods to do that:

    
    
        f <$> getNumber <*> getNumber
    

Monads are always particular cases of these type classes and you can show this
mathematically but its a bit complicated in practice because people added
monads to Haskell before they discovered these otehr type classes were
useful...

To wrap things up I would like to point out that most of the time monads and
functors are an "advanced" technique that lets you take programs that you
could already write before but write them in a neater way, with do notation,
fmap, <$>, etc. (While its true that you can write generic programs that
abstract over the monad interface, you only end up doing that if you are
writing a control flow library or something even more advanced like a parser
combinator). To give examples of what I am talking about, if you have
functions that return null on error you can already get by with adding an if
statements after every call (like you have to do in C) but monads let you omit
the boilerplate if statements. A more advanced example I like is
coroutines/generators (things that "yield"). You can write this sort of
program in a normal language by manually inverting your control flow, storing
state explicitly in stacks or other data structures. A continuation monad lets
you write the code in a more natural way (and as a library, without needing to
extend the base language with suport for a yield statement!)I think the
biggest problem with monad tutorials is that it seems that everyone finally
"gets" monads in a different way so the monad tutorials that get written lack
motivation. For example, I finally "got" monads when I was working with a
promise library in Javascript...

That said, one thing I noticed is that there are two main "types" of monad
tutorials. The first one is the the one taken by the OP and I call it the
"category theory" approach. It tends to show what sorts of things you can do
with monads and how they work mathematically but they kind of lack an
explanation for why you should bother with monads in the first place.

The other approach for monads is the "monads as an useful interface" approach.
One paper I would highly recommend in this category is the "awkward squad"
paper by Simon Peyton Jones:

[https://research.microsoft.com/en-
us/um/people/simonpj/Paper...](https://research.microsoft.com/en-
us/um/people/simonpj/Papers/marktoberdorf/)

Its an introduction to monads that helped me a lot and it also explains quite
a bit about the history of monads.

Which lets me come back to your main point:

> why this is better than the alternatives (like simple function composition)

Because simple function composition doesn't work! While nowadays people like
to point out that Haskell is a pure functional language, noone would have
bothered with making a pure language (back then) if all you gained was all
this headache with monads. The reason Haskell was pure in the first place was
because its original goal was to be a lazily-evaluated language and that
lazyness kind of forces you into pureness. You might think the order of
evaluation of function arguments in C being unspecified is bad, but Haskell
would be even worse since the order of evaluation for _everything_ is
unspecified and some things might not even end up being evaluated at all!

They ended up trying all sorts of approaches for adding effectful computation
to Haskell and the most successful one was the monad one (the SPJ paper goes
over this a bit). If you pay attention, the monad interface forces
computations to be single threaded (you can't "branch out" monadic effects)
and doesn't let you generally start or end an effectful computation wherever
you want, meaning that whoever defined the monad you are using can control how
effects are started or terminated via what functions they expose in the public
interface (Maybe exposes the constructors directly so you can pattern match on
them but IO makes it so `main` the only place you can start an IO computation)

Finally, this gets us back to notational problems. While monads make things
really neat in the type level and as a platform for effects, it can end up
really noisy in practice:

    
    
        getNumber >>= (\a -> getNumber >>= (\b -> f a b)))
    

So for monads we have "do" notation that lets you write things in a neater
way:

    
    
        a <- getNumber
        b <- getNumber
        f a b
    

This is really helpful and 99% of the time, do notation is precisely why
people bother making things into monads instead of defining their own versions
of (>>=), like they do in Javascript with promises. However, writing all those
intermediate variables can be annoying and doesn't feel "composeable". The
Functor and Applicative type classes provide methods to do that:

    
    
        f <$> getNumber <*> getNumber
    

Monads are always particular cases of these type classes and you can show this
mathematically but its a bit complicated in practice because people added
monads to Haskell before they discovered these otehr type classes were
useful...

To wrap things up I would like to point out that most of the time monads and
functors are an "advanced" technique that lets you take programs that you
could already write before but write them in a neater way, with do notation,
fmap, <$>, etc. (While its true that you can write generic programs that
abstract over the monad interface, you only end up doing that if you are
writing a control flow library or something even more advanced like a parser
combinator). To give examples of what I am talking about, if you have
functions that return null on error you can already get by with adding an if
statements after every call (like you have to do in C) but monads let you omit
the boilerplate if statements. A more advanced example I like is
coroutines/generators (things that "yield"). You can write this sort of
program in a normal language by manually inverting your control flow, storing
state explicitly in stacks or other data structures. A continuation monad lets
you write the code in a more natural way (and as a library, without needing to
extend the base language with suport for a yield statement!)

------
sesqu
That last picture was problematic in that it showcased three different
conventions for application:

    
    
      fx
      f <*> x
      x >>= f
    

The first of these (see also the Monads section) was the author's fault
(should have used f <$> x), but the last conjures memories of PHP. Something
to expand on? I would have also preferred more reminders that the box is the
Functor/Applicative/Monad, not the function or the infix operator.

[1] <http://adit.io/imgs/functors/recap.png>

~~~
egonschiele
Whoops! I'll get this fixed shortly.

> the last conjures memories of PHP

As a non-PHP programmer I'm not sure what you mean.

~~~
sesqu
A common complaint of PHP is that ordering of the arguments is inconsistent.
So too here.

------
Ygg2
One thing that bothers me, but I found no explanation why - is there a way to
turn 'Just a' to 'a'? >>= seems like it does that but I only see it working
with function that returns a Monad.

EDIT: I may have skimmed a bit.

~~~
DanWaterworth
Just think about it. Let's call such a function fromJust:

    
    
        fromJust :: Maybe a -> a
        fromJust (Just x) = x
        fromJust Nothing = ??? -- What can we put here?
    

Haskell doesn't have a null value that we could use here. There is such a
function defined in the standard library, but it is partial. It uses error in
the Nothing case.

edit: In many cases there is a clear value that should be used in the Nothing
case, suppose you have a Maybe Int which is the number of times a specific
event happened in the last second. Clearly 0 is a good default and you can
write a function (Maybe Int -> Int). There's a function I tend to use for such
cases: (maybe :: b -> (a -> b) -> Maybe a -> b). In this case:

    
    
        maybe 0 id :: Maybe Int -> Int

------
gioele
Have we got a new _why?

~~~
obviouslygreen
If so, let's hope it's more content and less drama... and please, let's
concentrate on the content and not some odd analogy that will only take away
from said content.

~~~
gioele
Mine was a compliment, not only an analogy.

The tech field needs people that are both confident with words, fun and have a
deep understanding of the inner details of things.

_why was such a writer, let's hope Adit (correct?) is another.

Regarding form vs. content, I think that the HN community enjoy discussing
form as much as content. Most of the people here are interested in design or
typography, things that more close to form than to content.

~~~
obviouslygreen
I agree that the tech community could benefit from more well-versed people
with insight into our tools and paradigms... however, I don't agree that _why
is such a person.

He openly claims to be a poor programmer, and as an artist... well, my
contention is that he appeals to a very limited audience. That's not a
criticism, simply a suggestion that while he may be valuable in an artistic
sense to some people, he's not contributing to the "tech community" in a
meaningful way. By which I simply mean that he should not be a yardstick by
which we measure anyone.

Let him be himself. Let the rest of us be ourselves. Let that be good enough.

~~~
steveklabnik
> Let him be himself. Let the rest of us be ourselves. Let that be good
> enough.

_why even said this himself:

> caller asks, “should i use hpricot or nokogiri?” if you're NOT me: use
> nokogiri. and if you're me: well cut it out, stop being me.

~~~
obviouslygreen
Hard time arguing with that. :)

------
alok-g
Posted earlier here: <https://news.ycombinator.com/item?id=5575334>

------
klrr
If you learn Haskell without getting into these stuff directly it's soo much
easier. It will obvious and not cryptic like if you tackle this without any
basic understanding of Haskell.

This guide is for understanding the theory behind monads, the use should not
be something difficult to get if you have used the IO monad before.

------
nickknw
I for one thought this was a fantastic article, and quite well-written.

~~~
egonschiele
Thank you!

------
osmano
good tutorial.

------
benched
The pictures look cute and friendly, but to me it read like a series of non-
sequiturs or deus ex machinas. I don't know Haskell.

