Hacker News new | ask | show | jobs
by DagAgren 2230 days ago
I am convinced that monads induce a very specific kind of brain damage that makes a person incapable of ever explaining monads.
4 comments

Start with a container.

    M a
Then add a way to put things in the container.

    a -> M a
Then add a way to use the thing in the container.

    M a -> (a -> M b) -> M b
Well, you see, that's one of the problems... monad implementations don't have to be "containers", or at least not the way most people mean. This was one of the critical errors in many of the aforementioned "tutorials". IO, the quintessential monad, is not a container, for instance.

(A nearly-exact parallel can be seen in the Iterator interface. You can describe it as "a thing that walks through a container presenting the items in order"... and yeah, that's the majority use case and where the idea came from... but it's also wrong. What it really is is just "a thing that presents items in some order". It doesn't have to be from "a container". You can have an iterator that produces integers in order, or strings in lexigraphic order, or yields bytes from a socket as they come in, or other things that have no "container" anywhere to be found. If you have "from a container" in your mental model then those things are confusing; if you understand it simply as "presenting items in order" then having an iterator that just yields integers makes perfect sense. A lot of the Monad confusion comes from adding extra clauses to what it is. Though by no means all of it.)

I wouldn't over-think it and over-describe it.

The "aha" realization that the "container" can be an ephemeral concept and not resident at run time can come later.

FWIW, I think of IO as a container: it contains the risk of side-effects within. All the examples you gave are containers in their own way.

The problem is telling people it's a container is "over describing" it. We don't need to hypothesize about that. We have the space suits and burritos to prove it is not a good didactic approach. It is not removing from the definition to simplify, it is adding to the definition, exactly as I carefully showed in my description of "Iterator". An Iterator is "a thing that presents a series of items". It does not simplify the discussion of Iterator to say "It's a thing that presents a series of items out of a container, but also, it doesn't have to be a container". It's not the first definition that's "overdescribing", it's the second.
Containers make sense.

Abstract computer science doesn't.

Part of why Haskell appears like such an implacable curmudgeon is the predilection of its community to believe that users must grasp type and logic theory to use it.

They don't.

Just like they don't need to have a mental model of their computer to write software for it.

In my experience, not having a mental model of the computer you are going to run your software on will bite you on the ass sooner or later.
I'll just point out that neither of you have managed to really take a single step towards actually explaining monads.
I'm not trying, so that's not a surprise.

This has inspired me to try to update my post on the idea in a side window, but it's been sitting on my hard drive for over a year now and probably still has a ways to go yet.

Yes, well, good example.
Pretty sure my point still stands.
I've had good luck with explaining it as a characteristic of a programming language. In a language consisting of sequences of statements with bindings and function calls, we expect that

f(x)

is the same as

a = x; f(a)

and the same as

g = f; g(x);

That's the monad laws. Whatever craziness you want to put in the semantics, those are properties you probably would like to preserve in your language.

I think I understand monads less now.
Do you really understand them less, or has it dislodged ideas that you thought were true? Moving towards zero is not always decreasing.
I'm no expert, although I think I remember that a Monad is basically just like allowing a sequence of statements to be executed. Like executing a code file ;)

Functional languages are really weird, for instance it's possible to switch line order of statements and the compiler will still figure out how to stitch that together. I think even JS in parts has or at least had that behaviour. (Actually that's useful when having mathematical formulas that are interdependent and you're too lazy to order them topologically by dependence)

On the other hand, just executing a sequence of commands in order to do I/O is only a normal thing to do since recently as far as I understand. The sweet spot for FP is IMHO something like React where state is strictly separated from the functions. (Imagine writing Hello World using Normal Maths)

(Please correct me if I'm wrong, which is probably quite likely ;))