Hacker News new | ask | show | jobs
by pka 2517 days ago
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

2 comments

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.

If you have a macro then something like "do notation" could be done in a superset of JavaScript, such as something like this:

  const listMonad=(m,f)=>[].concat(...m.map(f));
  const x=listMonad*>{
    let a=yield [1,2,9];
    let b=yield [3,4,5,7];
    return a-b;
  };
Such a code can then be compiled into a ordinary JavaScript code.
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.
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.
The problem is that being "flatmappable" very strongly implies that it's a container, but it doesn't have to be. It especially privileges the list implementation over other things. It's much the same as saying "Iterator may be a bit difficult to handle, but it's basically a way of presenting the elements of an array one at a time"; it isn't that that statement is wrong, it's that it is incomplete, and really leads people to thinking that iterators can only iterate on things you concretely have in memory, when instead they can iterate on anything (integers in sequence being popular), and there's also an "advanced mode" where you can drive the iterator (frequent example: an iterator to walk over files on the disk that can be fed back to to not descend into a particular directory upon inspection) that you'll never understand if you see iterators as "a way of walking an array".

You can understand flatMap and still not really understand "Monad", just as you can understand array iteration and still not necessarily understand "Iterator".

> The problem is that being "flatmappable" very strongly implies that it's a container

Can't fully agree with that, although I see your point. "Mappable" doesn't imply a container more than Functor's fmap method implies that all functors are containers. And "flattening" something is basically Monad's join.

But still, even if "FlatMappable" has a strong container connotation, I think it's good enough for establishing a first intuition. I think that once one shows what flatMap looks like in terms of e.g. Promises, the notion that it's about arrays or containers will vanish pretty quickly.