Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Why do monads come up so often when people talk about FP? Is it a meme or are they really an important and difficult to understand concept?


There is a myth that "monads" are magical insights of some sort -- it's not.

Difficult to understand: likely yes because the myth is not groundless. What "monads" capture is how to combine things with a lot of ceremony: (0) the things that we want to combine are sharing some structure/properies (1) we can inspect the first thing before deciding what the second thing is (2) we can inspect both before deciding what is the resulting combination. What requires a lot of thought is appreciating why "inspect, decide, combine" are unified in a single concept.

Important: indeed, because in Haskell-like languages monads are pervasive and even have syntactic primitives. It's also extremely useful when manipulating concepts or approaching libraries that implement some monadic behaviour (e.g. promises in JS) because the "mental model" is rigorous. If you tell someone a library is a monadic-DSL to express business rules in a specific domain, you're giving them a headstart.

Some final lament: there's a fraction of people who found that disparaging (or over-hyping) the concept was a sure-fire way to yield social gain. Thus, when learning the concept of monads, one situational difficulty that we should not understate is that one has to overcome the peer-pressure from their circle of colleagues/friends. Forging one's understanding and opinions takes more detachment than the typical tech job provides.


Many important "effects" (e.g. non-determinism, IO, asynchrony, environmental context, state... etc) are modelled as monads in Haskell.

You _can_ write Haskell code without understanding what a monad is, but composing and creating these things is going to be a little painful without that understanding.

Additionally, it seems to be a harder concept to grasp than e.g. functors or monoids.

I think this can be partly attributed to many Haskell programmers first being introduced to monads that are less than ideal for understanding the concept.

Shameful plug, I've written some thoughts on this here: https://frogulis.net/writing/async-monad


I think this was some amazing insight. I understand monads and I can use/write them but I feel like the concept hasn’t FULLY clicked yet. While even now it hasn’t, I feel like your blogpost got me that step closer to it, so thank you!


If you want a lazy-by-default language, you need to deal with a problem — laziness means you don't need to actually evaluate the reads until you use `a` and `b`, and the print uses `b` before `a`, so the two reads can be executed in reverse order:

    a = read()
    b = read()
    print("{b}, {a}")

One of Haskell's original goals was precisely to be lazy-by-default, which necessitated coming up with a way to solve this problem, and monads are the solution they came up with that gave us reasonable ergonomics. From a practical point of view, monads are just types that have reasonable implementations for three simple functions: `pure`, `map`, and `flatten`

    # lists as monads:
    pure 1 = [1] # put a value "inside" the monad
    map f, [1, 2, 3] = [f(1), f(2), f(3)] # apply the function to the "inside" of the monad
    flatten [[1], [2, 3]] = [1,2,3]  # take two "layers" and squish them into one
    
    # also, the simplest, but least useful, way to use functions as monads:
    pure 1 = (x -> 1) # putting a value inside a function is just giving you the constant function
    map g, f = (x -> g(f(x))) # map is just composition
    flatten f = (x -> f(x)(x)) # you squish by returning a new function that performs two nested calls
("reasonable" here largely means "they follow the principle of least surprise in a formal sense")

The trick is that, once you know what monads are, you can use them in any language (with varying degrees of support), and you can see instances of them everywhere, and it's an incredibly useful abstraction. Many common patterns, (like appending to a log, reading config, managing state, error handling) can be understood as monads, and compose quite well, so your program becomes one somewhat-complex data type, a handful of somewhat-complex functions that build an abstraction around that data type, and then lots of really small, really simple functions that just touch that abstraction. I have a .class parser written in Scala that exemplifies this general structure, need to put it up somewhere public.


I think one of the reasons for the meme is because there's so many monad tutorials. When a Haskeller is introduced to monads they'll run across all these monad tutorials with abstruse analogies for what a monad is. Is a monad a burrito? Or a space suit? Odds are that none of these analogies will make much sense and the programmer will have to figure out on their own, what is the deal with monads after all. At some point they might have an epiphany; monads are the sort of idea that is actually pretty neat when it "clicks". They will feel compelled to write a monad tutorial, and thus history repeats itself.


I'd say both.

For me the difficulty comes from the formal explanations/definitions, those always manage to confuse me. Result & Option types seem to have something to do with it so I may already have some understanding of the concept. But many explanations containing the word Monad also contain various other abstract mathematical terms. Trying to explain the concept to someone without mathematical background can be tricky.


public static void main(String[] args)

Why does this come up so often when people talk about Java?

Because beginners are confronted with the IO Monad if they want to write a Hello world program.

Monad is a typeclass that any datatype can implement. Monads have a then or flatmap like function that takes a Monad and a function that operates on the contents of the monad but also returns another monad of the same type which is then combined according to the implementation details of the specific monad that implements the monad typeclass.


They are used pervasively in Haskell, less so in other functional languages.

In Haskell, you can't write a "Hello world" program without using monads, so you cant really avoid learning about them.

IMHO monads are only really useful in Haskell because it has specific built-in syntax sugar to support them. Without this syntax sugar, they would be very cumbersome to use. So it's not really the monad type per se which is interesting, it is the code style which the syntax sugar enables.


And the reason you can't write "Hello world" in Haskell without using a monad is that functions in Haskell are "pure", meaning they cannot have side effects like outputting to the console.

Preventing side effects, including reading and writing global state, helps prevent bugs and makes it easier to understand and refactor Haskell code. Some would argue that the extra layers of abstraction from category theory and unpredictable order and number of lazy evaluations can actually make it harder to understand and refactor Haskell code.

Anyway, in order to perform I/O in Haskell, you evaluate your pure functions as a sequence of actions that are executed by the Haskell runtime. The construct that helps you build the sequence of I/O actions and allows you to bind their intermediate values to arguments to be used by subsequent actions is called the 'IO' monad.


While it's true that `IO` in Haskell has a `Monad` instance, you don't really have to know that to do `IO` in Haskell. Certainly you don't need to know `Monad` in the abstract to use `IO` concretely.

I like Haskell's `do` notation, which is its syntax sugar for monads, but it's really not that bad without. For example:

    do name <- getLine
       putStrLn ("Hello " ++ name)
isn't really that much nicer than:

    getLine >>= \name-> putStrLn ("Hello " ++ name)
or even:

    getLine >>= \name->
    putStrLn ("Hello " ++ name)
The main reason that monads are important in Haskell is that programs that do IO simply are not functions in a mathematical sense. If Haskell were limited to functions, it wouldn't be able to do IO.


You do need to understand the mechanics of monads in order to do even simple IO. E.g you can write:

    do name <- getLine
       putStrLn ("Hello " ++ name)
But you cant write:

    do name <- "Buddy"
       putStrLn ("Hello " ++ name)
 
You have to write:

    do let name = "Buddy"
       putStrLn ("Hello " ++ name)
Now try to explain that without basically explaining what a monad is.


I think it is because monads can be used for handling side effects, even though side effects do not exist in FP. :)


Because once you /get/ monads you see them everywhere.


They come up because people who don't understand them think they are important and people who do understand them want other people to know.

Also, speaking of memes: https://www.youtube.com/watch?v=ADqLBc1vFwI


Haskell uses monads as an escape hatch for performing side effects, so they come up often there.


Not as an "escape hatch" (that would be something more like `unsafePerformIO :: IO a -> a`), but as a principled way to compose (among other things) IO actions.

A Haskell program executes the IO action at `Main.main`, which must have type `IO ()`.

`putStrLn :: String -> IO ()` is a pure function - if you give it the same input, it always the same IO action as a result.


Monads are (for better or worse) contagious. So when one function calls a monad, then it needs to be included in the monad as well. It makes introducing memoisation to a file, randomisation, and memoisation not-to-a-file (without using lazy evaluation to express it) difficult. I don't know whether the alternatives (effect systems for instance) help with this.

I personally don't use functional languages because I find them too difficult given the needs and interests I have. I think about computations sequentially most of the time.


But then monads are a way to think about computations sequentially. If I write highly sequential code in a C-like language, in many cases most of the code is just boilerplate made necessary by the absence of native support for monads:

    int ret = doStepOne();
    if (ret == RESULT_OK) {
        ret = doStepTwo();
    }
    if (ret == RESULT_OK) {
        ret = doStepThree();
    }
    return ret;
would just be

    doStepOne() >>= doStepTwo() >>= doStepThree()
in a language with support for monads.


You can have support for monads without using them for IO.


Separating IO is kind of the killer feature, something like `rand()` is not an int unless you're reading dilbert[1] or xkcd[2]. Basic equations break down in the face of an Int-valued IO-action being conflated with an Int

[1] https://dilbert.com/search_results?terms=Random+Number+Gener... [2] https://xkcd.com/221/


Monads themselves aren't really contagious, it's the actions that would otherwise have side-effects that are, and also the fact they have to be executed in sequence.

This is also true for other things in other paradigms, such as async functions in javascript.

This is a good thing, however. In imperative programming, you have invisible temporal coupling. In pure-FP you have the same coupling, but it's exposed.


While I broadly agree with you, I think the post you're responding to has a point. You can't, in general, get a value back out of a monad, so if you call a monadic function, you may well have to return a monad. The obvious example is IO: there's no (safe) way get the `a` from `IO a`, so IO is kinda contagious.

Then again, there are lots of monads, such as `Maybe` and `List`, where you can get values out. These aren't contagious at all.

I agree with you that this is a good thing. Effects show up in the type signature - and it's all about those effects and managing them.


Yes, this is what I mean. IO is contagious for reasons unrelated to it being a Monad.


I think that applicatives and monads feel more contagious than they really are, because at first people tend to write functions that consume values of type `f a` too readily. This is because it takes some time to become comfortable with `fmap` and friends, so the new Haskell programmer often doesn't write as many pure functions.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: