
Functors, Applicatives, and Monads in Plain English - maxdesiatov
http://www.russbishop.net/monoids-monads-and-functors
======
VeejayRampay
I've found that trying to simplify things is an impossible thing to do in the
programming community (cue to "You're missing the point / You're
oversimplifying / This is not what a monad is").

It's a problem that stems from being surrounded by millions of people who take
disproportionate pride in the belonging to a savant elite. What I always say
to people who ask me to describe programmers: "Programmers are people who like
to create metaphors, but who like to correct other people's metaphors even
more".

That being said, I feel like I've progressed in my understanding of those
concepts by reading the post, so thanks to the author.

~~~
tikhonj
No. It's a problem because you, the reader, _are_ missing the point.

Being against cargo cults does not make you elitist. This characterizes _a
lot_ of people, and it's not fair or accurate to blindly ascribe them with
malicious motivations.

Think about a different area: people who try to "simplify" quantum computing.
What do we get? Mass confusion. The fact that so many people think that
quantum computers can solve all NP complete problems by trying every
possibility _is a real problem_ , not a consequence of "people who take
disproportionate pride..."!

And then people go on an try to build things on top of these ideas and end up
creating a mess because they started off on shaky foundations. Then that mess
gets enshrined in a popular library, it creates lots of problems for everyone.
Then programmers either live with those problems or conclude that the whole
functional programming "thing" is delusional and useless.

~~~
VeejayRampay
You're absolutely right. To be clear, I was not saying that everything can and
should be simplified, some things just warrant a certain level of complexity
because they're... well... complex and that's absolutely fine (your example of
quantum computing is great in that regard, though I supposed it COULD be
vulgarized for the masses to some extent, at least more than it currently).

I was addressing the difficulty I've seen in the programming community of
trying to make things more accessible when they actually can and should be.

~~~
TheOtherHobbes
I think there's a kind of naive reductionism that runs throughout CS.

You can avoid complexity by ignoring it in the proverbial Dunning-Kruger kind
of way - where you don't know what you don't know, but you try to turn your
toy misunderstanding of some domain into code anyway.

Or you can avoid it by creating over-systematised technologies that try to
barricade "impure" unpredictability behind a big wall of abstraction and
insider-only jargon.

They're both defensive moves against the messiness of the real world.

There may be other approaches that trade off accessibility with useful results
in other ways.

~~~
harveywi
This "naive reductionism" looks to me like the difference between the way that
analysts and algebraists work on problems [1]:

"If I have to wave my hands and explain it, I would explain it like this. In
algebra there are sequences of operations which have proven to be important
and effective in one circumstance. Algebraists try to reuse these operations
in different contexts in the hopes that what proved effective in one situation
will be effective again. By contrast an analyst is likely to form an
idiosyncratic mental model of specific problems. Based on that mental model
you have intuitions that let you carry out long chains of calculations that
are, in principle, obviously going to lead to the right thing. Typically your
intuition is correct to within a constant factor, and you're only interested
in some sort of limiting behavior so that is fine."

How else is a person supposed to _intelligently_ explore the solution space of
a problem other than through some kind of "naive reductionism"?

[1] [http://bentilly.blogspot.com/2010/08/analysis-vs-algebra-
pre...](http://bentilly.blogspot.com/2010/08/analysis-vs-algebra-predicts-
eating.html)

------
wrs
Thousands of blog posts later, I don't think the following has changed:

* Explaining these concepts in the abstract results in bafflement as to why anyone would care about these useless abstract concepts.

* Explaining only the concrete instances of these concepts results in missing the point that they are all instances of the same concepts (and that you can write reusable code at that level, which can be a big productivity improvement).

* The only way to understand both at once is to sit down and read/write a bunch of code until you understand both levels. And once you pass that point _you_ get it, but then you hit the above two roadblocks trying to find a shortcut to explain the concepts to someone else.

It reminds me of music theory, where you can bombard someone with all sorts of
concepts like scales, modes, chords, etc., but it's all nonsense until they
actually _play music_ for a while--then most of it is both understandable and
useful.

~~~
_halgari
Except some of us understand these concepts, see how they can be used, but yet
don't understand why we would ever use them.

In all these sort of articles I've never seen something that can't be done
simpler with a bit of mutability sprinkled here and there. Sure having
something 100% pure is great, but I don't know if I want that if it requires a
10x increase in complexity.

~~~
runT1ME
mutability increases complexity by 10x. Higher kinded types do not, they're
unfamiliar and a bit weird at fist, but end up _simplifying things_.

Abstractions make code _easer_ to reason about (Go read about parametricity!
really!).

Mutability doesn't even help you from a superficial level for some things.
Sure, if you're shoehorning a state monad somewhere, a locally scoped variable
might look easier. However, try to fix 'Futures' without monads? It's much
much less elegant.

------
MichaelBurge
His 'plain english' descriptions are really just the definition in Haskell.
Haskell defines one or two more methods for efficiency, but if you ignore the
entries with default implementations you get:

1\. "Functors are containers you can call map on. That's it. Seriously."

    
    
        class  Functor f  where
          fmap        :: (a -> b) -> f a -> f b
    

Historically, fmap in Haskell used to be called map. They gave it a different
name so that error messages would appear less abstract when working with
lists.

    
    
        fmap (+2) [5] = [7]
    

2\. "you may want to have a function in the container and apply it to value(s)
in another container of the same kind. "

    
    
        class Functor f => Applicative f where
          -- | Lift a value.
          pure :: a -> f a
    
          -- | Sequential application.
          (<*>) :: f (a -> b) -> f a -> f b
    

So at least in Haskell, he describes the sequential application operator but
leaves out the ability to lift a pure value to a container value.

    
    
        pure 5 = [5]
    
        [(+2)] <*> [5] = [7]
    

3\. "Monads are containers you can call flatMap on. Again, that's it."

    
    
        class Applicative m => Monad m where
          -- | Sequentially compose two actions, passing any value produced
          -- by the first as an argument to the second.
          (>>=)       :: forall a b. m a -> (a -> m b) -> m b
    

And here's how to use it like a flatmap:

    
    
        [1,2] >>= (\x -> [x + 5, x + 10])
        [6,11,7,12]
    

That's [1 + 5, 1 + 10, 2 + 5, 2 + 10] if you didn't catch that.

Usually these also come with some laws to help you reason about the typeclass,
but looking purely at the language level it can help to remember that a
Functor, Applicative, or Monad are nothing more than a type together with some
functions with a certain signature.

I find it's usually more useful for a beginner to just start writing code
without all the abstractions. Once they have a pile of code and start to want
to clean it up a bit, it's not too hard to start stacking some monad
transformers.

~~~
Buttons840
What's a monad transformer? I kind of know what a monad is, but what is a
transformer? Functional programming sure does use some scary words. I guess
OOP has it's fair share as well, with inheritance, subclasses, superclasses
(which are much more super than your lowly subclass apparently), etc.

~~~
mezuzza
I like to think of it as a way to combine monads. Two classic examples might
be the State monad and the Maybe monad.

So let's say you want to create a function that operates on some state and
might return a value. These are clearly both monads that you've heard about in
Haskell, but how do you use them together? The answer is to use a monad
transformer.

StateT is the state monad transformer that takes any other monad and adds
state to it. `StateT s m a' is a monad that adds a state of type s to a monad
m. So `StateT Int Maybe a' is thing that when given an int which is its state,
might return a value.

This particular combination was a little esoteric, so I'm not sure what a
great example of this would be. However, you could also think of a situation
where you want to have some sort of read-only state (such as flags from the
command line) and the ability to write to a log.

In Haskell these are provided by the Reader and Writer monads. But again, you
have two separate monads here and in order to actually make use of them,
you'll need to combine them somehow. Enter a monad transformer. You can either
transform a Writer monad with the ReaderT monad transform or transform a
Reader monad with the WriterT transform.

    
    
      ReaderT r (Writer w) a
      OR
      WriterT w (Reader r) a
    

Both satisfy this use case. I think those two constructions should be
equivalent and conceptually equal. Someone should correct me if I'm wrong and
there is some actual reason that those are different. To my understanding, the
difference is purely due to limitations of the language as opposed to the
mathematical constructs.

~~~
MichaelBurge
There's also the RWS monad, that rolls in Reader, Writer, and State one big
monad. It's useful for servers and other applications: Configuration is
Reader, changing state is in State, and log messages go to Writer.

------
proc0
As someone who has walked that path, being a complete beginner in functional
programming, learning via simple examples, such as the article, and then
finally learning (or perhaps beginning to grasp) the basics of category
theory, I can say that from my observation there's no substitution to the
actual mathematical perspective on the matter.

This point might have been made elsewhere, but I think the abstract nature of
such concepts discussed in the article, makes it hard for "partial
understanding" to be useful. In other words, for a beginner to read the
article, and then go and start using the concepts themselves, is very
unlikely. Either you learn the existing interface like the Maybe monad, or the
Reader monad, and you learn how to invoke its functionality, OR you have a
solid grasp of Monads as a concept and how it can be applied in any language,
which usually means some understanding of category theory. To JUST know that
Monads are containers that return "flattened" containers is useless. In other
words, you don't need to understand Monads to use them, and if you want to
understand them, then perhaps containers are a bit too bastardizing to give
you a full view of how and why they're a useful pattern.

~~~
draven
That reminded me of the introduction of the Typeclassopedia (
[https://wiki.haskell.org/Typeclassopedia](https://wiki.haskell.org/Typeclassopedia)
), which states one must understand the types and gain a good intuition of how
the typeclasses relate to each other (and "good intuition comes from hard
work, not from learning the right metaphor".)

------
kika
I don't know category theory, so I defer to more knowledgeable people here,
but that sounds strange: "Functor it understands how to take doStuff and
execute it on the value inside itself, then spit out a new Optional. You don't
lose the int-ness or optional-ness." So if I map a function, that takes an int
and returns a string over an array of ints, this array stops being a functor?
Or is it just a Swift thing?

And, as always, I urge people spend an hour of their life and get the
explanation of all this from someone who knows the math but still able to
explain this in plain English:
[https://www.youtube.com/watch?v=ZhuHCtR3xq8](https://www.youtube.com/watch?v=ZhuHCtR3xq8)
To the best of my limited knowledge, it's all 100% correct.

~~~
catnaroek
> this array stops being a functor?

An individual array, as a runtime object, isn't, never was, and never will be
a functor, as the term is used in this discussion. Rather, the functor is _the
type constructor of arrays itself_ (to be pedantic: the type constructor _and_
the `map` function). In the grammar of most programming languages, type-level
and value-level terms are kept separate, so not even in principle could you
conflate a type constructor (a type-level entity) with a runtime object (a
value-level entity).

<rant>

This is a very common problem. Most programmers are only used to reasoning
about runtime objects, like “lists”, “sockets” and “windows”. Inasmuch as they
talk about entities that don't have a runtime manifestation, like “types”,
“preconditions” and “invariants”, these words are merely used as shorthand for
things they want to say about runtime objects.

Understanding how concepts from abstract algebra and category theory are
applied to programming requires a change of mindset: the subject matter is no
longer “what happens to this individual object”, but rather “what is the
_structure_ common to a large class of objects” and “how can this structure be
used as the backbone of our algorithms”. This requires a new vocabulary in
which abstract structures (which describe or constrain the representation of
runtime objects, but don't have a runtime representation of their own) are
first-class entities, so that you can say “the type constructor of immutable
arrays is a functor” just as easily as you can already say “this runtime
object is an array”.

</rant>

~~~
kika
> this array stops being a functor

Okay, "the type of this array stops belonging to the typeclass of functors".
Better? Peace? :-)

I totally agree with your rant and I'm personally less than half way there
myself, but this particular comment was within the context of the original
article. I do understand the difference between the type and the value
(instance). It's not even very hard for C++ programmers.

------
bcherny
Nice writeup. I also like
[http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html).

------
yummyfajitas
Unfortunately, his simple explanation of these concepts is just not correct.
He claims functors are a container you can map on and that monads are also
containers.

Here's a monad (which is also an applicative functor) that isn't a container:
probability. The space of probability distributions is a functor which can be
mapped. In the sampling representation:

    
    
        p.map(f) = new Prob {
          def draw = f(p.draw)
        }
    

It also supports flatmap:

    
    
        p.flatMap(f) = new Prob {
          def draw = f(p.draw).draw
        }
    

Yet nothing is "contained". Each Prob object is just a random number variable
generator.

~~~
nandemo
Right. And function types aren't containers either. So much for "plain
English".

~~~
yummyfajitas
We should just accept that math is a better language for some concepts and try
to learn it. This idea that everything can and should be dumbed down for the
innumerate is anti intellectual.

------
draw_down
I appreciate the use of plain english, but it doesn't help understand why
these things are good or important. Like, ok, monads avoid double-
containering. That's... nice?

~~~
hyperpape
You could try "You Could Have Invented Monads" for an explanation of why
they're valuable [https://blog.jcoglan.com/2011/03/05/translation-from-
haskell...](https://blog.jcoglan.com/2011/03/05/translation-from-haskell-to-
javascript-of-selected-portions-of-the-best-introduction-to-monads-ive-ever-
read/) (linking to the JS version, which links to the Haskell version).

But while I liked that tutorial, I think what worked was time. I tried to
write Haskell and slowly got a feel for them. After awhile, I started to read
JS and Java code and think "oh, another monad".

~~~
haldean
Somehow in the last hour that link died. Here's a page with the same title,
but in Haskell: [http://blog.sigfpe.com/2006/08/you-could-have-invented-
monad...](http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-
and.html)

------
stevetrewick
I've read a ton of such blog posts recently in exploring this subject - seems
like the tradition is as soon as you believe you understand the monad you must
immediately write a blog post explaining it and pointing out why everyone
else's metaphor is too complicated.

This one is kind of nice in its succinctness, but that ends up meaning it
doesn't explain why I should care or what I would do with my monad once I have
grokked it.

I've yet to write my personal "how I grokked my monad and why everyone else is
wrong" post, but I suspect that there's probably a sweet spot somewhere
between 'monad is simply a monoid in the category of endofunctors' and a
'plain English' translation of the monad laws with no context. Preferably with
an explanation of why I would want or need such a thing up front. Preferably
without Haskell notation.

That said, such an intro would probably still be pretty hard going, some
things are just hard, or at least difficult to understand without context. I
said I've been reading lots of these recently, actually I've been reading them
for a couple of years, but I've only been _understanding_ them recently
because I've bumped into a problem domain for which the solution I was groping
for in the dark turned out to be a monad.

Swift wise, I found the following to be very enlightening

[http://alisoftware.github.io/swift/2015/10/17/lets-talk-
abou...](http://alisoftware.github.io/swift/2015/10/17/lets-talk-about-
monads/)

[http://alisoftware.github.io/swift/async/error/2016/02/06/as...](http://alisoftware.github.io/swift/async/error/2016/02/06/async-
errors/)

[http://khanlou.com/2015/09/what-the-heck-is-a-
monad/](http://khanlou.com/2015/09/what-the-heck-is-a-monad/)

[http://www.javiersoto.me/post/106875422394](http://www.javiersoto.me/post/106875422394)

[http://chris.eidhof.nl/post/json-parsing-in-
swift/](http://chris.eidhof.nl/post/json-parsing-in-swift/)

------
colordrops
When did "Swift" become "Plain English"?

