Hacker News new | ask | show | jobs
by embwbam 1069 days ago
They’re like promises or futures, but limited to chaining only one type of thing:

Maybe (optionals) - chain steps and abort if any step is empty.

IO - perform IO side effects, and run the next step when this one completes. Just like a async/await.

You use monads all the time in other languages! Haskell just has many more kinds and allows the programmer to make their own.

I always twitch when I hear someone say you can’t both understand and teach them. Did I succeed?

Also note that you don’t have to totally understand monad machinery to use them productively, only to write your own.

2 comments

> You use monads all the time in other languages!

No you don't. You are confusing monads with features which can be implemented using monads. In Haskell monads are used for modeling side effects, but this is not the case in other languages. In Haskell exceptions are implemented using monads, but this is not the case in other languages.

Yes you do. `flatMap`ping an array in javascript is using a monad every bit as much as `join`ing a linked list in Haskell. `Promise`s would be monads were it not for the (inexplicable, terrible) decision to make it impossible to nest them. C#'s `IEnumerable<_>` is not quite a monad, but only because it doesn't have a preferred implementation. Pick one, and it's as monadic as `Maybe` or `Either`. And of course function types are always monads.

What these languages don't have and Haskell does is a way to talk about all monads, all at once. Instead of

    replicateM :: forall m a . Monad m => Int -> m a -> m [a]
you have to separately implement

    replicateArray: <T>(count: number, ts: T[]) => T[][]
    replicateFunction: <S, T>(count: number, f: (s: S) => T) => (s: S) => T[]
    replicatePromise: <T>(count: number, t: Promise<T>) => Promise<T[]>
and so on.
Are you suggesting that flatMap is used all the time in JavaScript?
I find promises and futures in other languages (python, JavaScript, rust) close enough to how monads work to gain a very productive working intuition for them.
But how would you e.g. explain the monad laws from this intuition? Promises in JavaScript are not monads and does not obey the monad laws.
You don’t need to explain the monad laws to a beginner. They don’t need to know what bind does. All they need to know is how to use do syntax, and enough of how it works to use IO, Maybe, State, and a few others.

After they’ve been working for a month or so, they’ll “get” monads on a gut level, and the details won’t be so confusing to them.

Yeah, learning by example and by doing is the best way to learn any programming concept. But using misleading and confusing analogies along the way will just make the learning process harder, not easier.

Explaining monads in terms of promises and futures will make a beginner associate them with asynchronous programming, which might make some sense for some monads. But now show such a beginner a simple list comprehension and explain it is also a monad, and I assure you they will be very confused! Explaining concatMap in terms of promises is a very convoluted way to explain something quite simple.

I see what you’re saying. I agree that abstract metaphors confuse people. But clearly, the way we are teaching monads right now isn’t very effective for most people.

Haskell comes from a population that loves to understand things from the ground up, and most people learn by doing.

So, what if, instead of an abstract burrito metaphor, we taught people 4 monads: List, Maybe, State, and IO. We explain how do syntax does “something different” for each of them, and how IO is very similar to promises, but the others aren’t. Then we let them play with those for a few weeks, submit some PRs, let the panic of not understanding subside, and then teach them the theory?

Think about how JavaScript developers learn promises. They don’t understand how they work under the hood at all when they first start using them. Then, when they need to go deeper and look under the hood, that theory is connected to their practice, and makes much more sense to them.

Humble question:

> Maybe (optionals) - chain steps and abort if any step is empty.

Could this be (remotely) akin to the following shell pattern?

    set -o pipefail
    cmd1 | cmd2 | ...
This would either return the result of a successful execution of tje whole pipe, or return at the first command erroring out.

Does it make any sense?

Yes that’s exactly what the Maybe monad does! It would look like this in Haskell code

    doStuff :: Maybe X
    doStuff = do
      r1 <- cmd1
      r2 <- cmd2 r1
      …
      return rX
Which is syntactic sugar for:

    doStuff = cmd1 >>= cmd2 >>= …
A sometimes important difference is that every process in the pipeline is spawned at the start, and may operate on partial input. A function returning Maybe needs to complete before we know whether it returns Just or Nothing, and we can't start the next function until we have the Just in hand, as that's the input to the function.