
Inventing Monads - tambourine_man
https://blog.kabir.sh/posts/inventing-monads.html
======
peteretep
> I'm fifteen years old with good knowledge of mostly high school level math,
> and may have missed some parts.

Kabir,

Great work on getting this done. I wonder if looking around for and mentioning
the similar "You Could Have Invented Monads"[0] would be a good move

[0] [http://blog.sigfpe.com/2006/08/you-could-have-invented-
monad...](http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-
and.html)

~~~
charles_f
I know "20y of professional practice in the industry expert"s who would look
at you with a blank stare for such mastery of non-trivial abstractions, and
yet not get it.

Good job is what I mean

~~~
rhizome31
As one of these guys (not yet 20 years experience but close), I concur. I
don't accept defeat though as I'm actually trying to learn FP for the third
time! I bookmarked this blog post for when I get to monads again.

Actually I'm fine with "lightweight" FP like Scheme or Elixir (HOFs,
recursion, immutable data, etc.) but still struggle with "real" FP like
Haskell or functional Scala (algebraic data types, monads, lenses, etc.)

By the way I couldn't help to notice that the blog also features a CSS
centering article. The author really is into esoteric concepts!

~~~
scns
for a good explanation what ADTs are useful for watch Effective ML by Yaron
Minsky from 18:00
[https://www.youtube.com/watch?v=-J8YyfrSwTk](https://www.youtube.com/watch?v=-J8YyfrSwTk)

------
diegoperini
> Think of monads as a way to overload a semicolon. It might sound a little
> crazy at first, but imagine being able to override the semicolon to reduce
> boilerplate in specific code blocks. That's basically how monads are used in
> practice.

Lol, this resonated well. It is both brilliant but also extremely confusing
(talking with experience). In my previous company, when we collaborated while
researching Monads, we ended up in this conclusion as well. Some even argued
that Monads actually override the indentation before an expression to turn it
into a statement which doesn't help at all either.

------
widdershins
That's a pretty good explanation, thanks. Along with the monads in Clojure [0]
article I read recently, I feel I'm getting a decent grasp on monads finally.

Now they're beginning to click, I don't feel they're too complicated. Simply a
way to insert a converter function between each stage in a good-old-fashioned
pipeline of function applications? The converter takes the output of each
function, transforms it so that it can be piped into the next function, then
takes _that_ output and combines it with the previous one, etc. Please,
someone enlighten me if I've got the wrong end of the stick.

[0] [https://github.com/khinsen/monads-in-
clojure/blob/master/PAR...](https://github.com/khinsen/monads-in-
clojure/blob/master/PART1.md)

~~~
mikekchar
You have it right.

It is literally a container that allows you to pipeline functions on its
contents. The only technical catch is that you aren't allowed to change the
type of the contents in the function. That means if you start with something
of type A in the container, you have to get a container containing something
of type A at each stage of the pipeline.

There are probably 3 popular uses for monads. First to defer error handling.
You allow type A to include some kind of error state. The you run your entire
pipeline of functions, but at every stage if the container contains an error,
you just return a container with the same error as output. That way, none of
your pipeline stages need to check for valid input -- they just assume there
were no errors before their stage.

The second main case is to defer processing until a later time. You have a
pipeline of functions and a container. As long as you don't look at the result
of running that pipeline, even if the container doesn't contain anything yet
you can pretend that it represents your result. You can pass that along in
your program, gradually appending more functions on your pipeline as you go.
When you finally put something in the container, you can see what the result
is. This is useful when you don't know what your result is right now, but you
know what processing you want to do when you get the input (for example, if
you are going to query some external system and when it returns you want to do
some processing. You can go ahead and pretend to process that result and when
the data finally arrives, then you can actually get the result of the
computation).

The third main case is when you have a big list and you want to narrow it. For
instance, you might have a list of food. You want to narrow that to a list of
fruit. Then you want to narrow that to a list of red fruit. Then you want to
narrow that to a list of red fruits that are berries. Instead of writing a
function to do your whole query, you can separate it: A query for fruit. A
query for red. A query for berries. This can make your code a lot easier to
read.

There are other things you can do with monads, but that's the basics IMHO.
They really are simple, but suprisingly enlightening. You might think, "This
constraint that I return the same type each time is kind of annoying", but if
you spend a fair amount of time with monads you will see that this allows you
to make pretty amazing assumptions in your code -- which, in turn, let you do
very powerful things.

~~~
Patient0
" The only technical catch is that you aren't allowed to change the type of
the contents in the function. That means if you start with something of type A
in the container, you have to get a container containing something of type A
at each stage of the pipeline."

I don't think this is correct. What doesn't change is the overall type
constructor (e.g. if you start with a list, you'll still end up with a list...
If you start with an IO action, you'll still have an IO action). But the type
that is wrapped by the container can and usually does change. e.g. if you
"map" over a list of A's, you'll get a list of whatever type your map function
returns - still a list, but a list of B's, not a list of A's. fmap :: (a->b)
-> [a] -> [b].

------
pka
If you can implement flat_map [0] for something, it's (probably) a monad. You
might as well call monads FlatMappables.

That's the simplest, most understandable and most accurate explanation for an
initial intuition I've come up with so far.

[0]
[https://apidock.com/ruby/Enumerable/flat_map](https://apidock.com/ruby/Enumerable/flat_map)

~~~
lmm
It needs to be associative. More concretely, if you want to have something
like "do notation" that makes sense, so that you can write code like:

    
    
        x <- f(a)
        y <- g(b)
        z <- h(c)
    

for composition, then it needs to be the case that x.flatMap(a =>
f(a)).flatMap(b => g(b)) is equivalent to x.flatMap(a => f(a).flatMap(b =>
g(b))). Unfortunately people sometimes implement a flatMap where these are
almost, but not quite, equivalent, which leads to very confusing behaviour and
subtle bugs.

You also need to be able to "point" a pure value and have the equivalences
you'd expect - this often ends up being important as e.g. the base case of a
recursive function.

~~~
pka
Sure! But note I said "initial intuition". return/pure and monad laws can be
explained later, once one understands that there's really nothing magical
about monads.

~~~
lmm
Of course there's nothing magic. But I think it's important to grasp the
definition as a whole rather than thinking monads are "just" some subset or
superset - the full definition of a monad is quite short and every part of it
is vital, even if it doesn't seem so to start with.

------
Izkata
Just want to say, this guide has one massive advantage that's usually missing:
multiple monads.

Far too many tutorials I've seen in the past will pick only one to use as an
example, then act as though that specific monad's functionality applies to
monads in general, which just confuses the issue.

------
proc0
Monads are not invented, they are discovered multiple times. You invent
toilets and gadgets, but every time you come across a mathematical object, if
it really is that object (with proofs), then it must be the same one (a.k.a.
diagram commutes).

------
juped
Wow, I haven't seen a monad tutorial in over a decade. Very nostalgic post.

------
leo89
Good attempt, it's good thing to popularize powerfull concepts in programming,
which are not as hard as most programmers think they are. However I have one
big problem with this article. Code snippets are so horrible. Seems so
unpractical. Perhaps because I never used functional features of Javascript?

~~~
kbr
Hey! I attempted to make the code snippets use practical concepts, but they
aren't very practical for use in actual JavaScript code — the reason being
that JavaScript is not a functional language and doesn't have syntactic sugar
for monads.

However, I chose JavaScript because I believe it's more familiar and
widespread, and it can be easier to explain a foreign concept when it's done
in a language that more people feel comfortable with.

~~~
garmaine
Interestingly for someone like me who has had the luxury of never having to
read or write a line of JavaScript, it was all alien gobbledygook. Whatever
that code was supposed to be doing was not clear to this C++ programmer.

~~~
kbr
Sorry about that haha, the style of programming in this guide is _very_
different from C++, and it relies heavily on currying and lambda functions
using next-generation JavaScript syntax. Nonetheless, I think it can offer a
new perspective because JS is more comfortable than Haskell for most
developers.

~~~
garmaine
Oh I'm fine with all those concepts. C++ has anonymous functions, and I'm a
Haskell programmer too. But whatever syntax JavaScript uses is not standard
across other imperative languages.

~~~
kbr
My bad then, that's fair. JS has been deviating from C-style syntax and it
really shows in this article haha

