Monads Are Too Powerful: the Expressiveness Spectrum
Posted3 months agoActive3 months ago
chrispenner.caTechstory
calmmixed
Debate
80/100
Functional ProgrammingMonadsHaskell
Key topics
Functional Programming
Monads
Haskell
The article argues that monads are too powerful and explores the expressiveness spectrum in programming, sparking a debate among commenters about the usefulness and limitations of monads.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
4d
Peak period
51
84-96h
Avg / period
16
Comment distribution64 data points
Loading chart...
Based on 64 loaded comments
Key moments
- 01Story posted
Oct 12, 2025 at 1:11 AM EDT
3 months ago
Step 01 - 02First comment
Oct 15, 2025 at 4:24 PM EDT
4d after posting
Step 02 - 03Peak activity
51 comments in 84-96h
Hottest window of the conversation
Step 03 - 04Latest activity
Oct 17, 2025 at 4:00 AM EDT
3 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
ID: 45555426Type: storyLast synced: 11/20/2025, 2:21:16 PM
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
Granted, I also don't have as heavy an attachment to pure functional as most people seem to build. Don't get me wrong, wanton nonsense is nonsensical. But that is just as true in immutable contexts.
As for ‘compiler’ you also don’t need to go all the way to bare metal, some runtime like WASM or the JVM which is more civilized is a good target these days.
That is, a lot of why you would go with macros in the past was to avoid the expense of function calls. Right? We have so far left the world of caring about function call overhead for most projects, that it is hard to really comprehend.
Coroutines still strike me as a hard one to really grok. I remember reading them in Knuth's work and originally thinking it was a fancy way of saying what we came to call functions and methods. I think without defining threads first, defining a coroutine is really hard to nail down. And too many of us take understanding of threads as a given. Despite many of us (myself not immune) having a bad understanding of threads.
That is, a basic class that has defined state and methods to modify the state is already enough to explain a state machine. What makes coroutines better for it?
> the state is already enough to explain a state machine.
I did not talk about explaining state machines but implementing state machines as coroutines. Progression: give an idea of state machines, show how hard is to handle state, present coroutines as way to handle states.
Also, there's always plenty of use for doing work at compile time.
In some sense they can also be seen as a better code generation.
> screwing around with functions and macros doesn't hold a candle to what you learn from the Dragon Book.
---
So, what is it that you learn from that book that's a revelation for you compared to the weak beer of composable effect systems?
This depends a lot on what you mean. My first take is that the more you know about macros the more you realize what they can do.
I don’t know what your takeaway from the Dragon Book was, but writing DSLs using macros feels very usefully powerful to me.
I think you are undervaluing modern macros.
So while it is true, that what he has described so far is not sufficiently powerful for normal programs, he has clearly stated that there are more abstractions between Applicative and Monad to explore than what he has presented so far.
Haskell and ML make up one of the major language families. They are more like each other than they are like other languages. Inspired by the lambda calculus. Strong static typing with type inference. A succinct math-like syntax that emphasizes pattern matching.
Haskell goes further with syntactic sugar and tries to be almost equation-like:
SML: But F# like Haskell makes no distinction between values and functions: The use of indent-based blocks is another Haskell-ish influence on F#. But now we're awfully close to bikeshedding.https://news.ycombinator.com/item?id=1396763
https://hackage.haskell.org/package/base-4.21.0.0/docs/Contr...
https://book.realworldhaskell.org/read/monads.html
A common metaphor for monad is "executable semicolons". They are effectively a way to add (structured) hook computations (that always returns a specific type of value) to run every time a "main" computation (akin to a "statement" in other languages) occurs "in" the monad.
It's sort of like a decorator in Python, but more structured. It lets you write a series of simple computational steps (transforming values), and then "dress them up" / "clean them up" by adding a specific computation to run after each step.
Typeclasses are a distraction, the point is computation ignoring annoying contexty stuff (file not found errors, null on failure, etc) and there's dozens of examples in literally every language ever.
Not all problems are solved with a technical definition.
Where did you get the impression that Haskell people don't realize that code patterns are a thing in other languages, or that monads don't exist in other languages?
Besides the syntactic sugar of "do notation", what Haskell has that most other languages don't have is the ability to abstract over monads, since it has higher kinded types. That is, you can write a generic operation like
that works for all monads. Most other type languages force you to write mapM separately for each monad you want it to work with.(but do I appreciate the effort you put into your reply - reading that monad's are more like interfaces is new information to me, and might help down the road)
Examples of monads are Promises and Elvis operators (for values that can be nullptrs). In a sense exceptions as well. Having heard this I think if you do a second pass at the type definitions, I think you may be able to parse them out
It really is just "a wrapper for values that sticks around when you do something to the values".
Think of it as a coding pattern, and it's much easier to grok
It's handy for IO because, well, did you see the examples I gave? A monad lets you basically ignore the failure mode/weirdness (the async stuff in the context of promises, null type in the case of Elvis) and worry about the computation you actually want to be doing.
Other places you might apply these would be fileio (file not found? cool, don't care, deal with it later) or networking (as long as the connection's good, do this.)
Promises are wrappers, I see that. How are exceptions wrappers? They stop the code at the point the exception occurs and unwind the stack. They don't allow you to continue doing stuff and deal with it later.
> A monad lets you basically ignore the failure mode/weirdness
I just don't see how this works out in practice?
Ok, I defer dealing with file not found. Does that mean I know perform heavy computation and parsing on something that does not even exist. Wouldn't it be way easier to just do an early return and encode that the file exists in the type? And how does it play out to let the user wait for you doing something and you at the end coming around, well actually the input file was already missing?
And then you have some intermediate layer that needs to store all the computation you are doing until you now whether something succeeded or not. All this to save a single return statement.
It's not about saving a single return statement, even in the elvis case. How many times have you written code along the lines of "if this isn't null do this, otherwise return. If the result of that isn't null, do this, otherwise return" etc etc.
Elvis ops are a small QoL change, Promises are essential to async Exceptions (much of the time) are kind of a "catch it pass it on" logic for me, and man do I wish I didn't need to write it every time.
With networking this really shines, or really just anything async etc
Yeah that sucks, but why would you write it that way. I thought it is common to write "if this is null return, anyways: do this, do that."
> Promises are essential to async Exceptions
I don't see that either. If errors are specific to functions then there is only one case where I handle them, so it doesn't save something to put these checks elsewhere. If they can be accumulated over many calls, then they should be just part of the object state (like feof), so I can query them in the end.
A monad is a function that can be combined with other functions.
It's a closure (or functor to the cool kids) that can be bound and arranged into a more complex composite closure without a specification of any actual value to operate on.
It's a lazy operation declaration that can operate over a class of types rather than a specific type (though a type is a class of types with just a single type so this is more a note on potential rather than necessary utility) that can be composed and manipulated in languages like Haskell to easily create large declarative blocks of code that are very easy to understand and lend themselves easily to abstract proofs about execution.
You've probably used them or a pattern like them in your code without realizing it.
You need to be able to "wrap" values and then also "wrap" functions in the way you expect. That's literally it.
Btw, the list monad example is stupid imo and borderline misleading. The promise/nullable/Either examples are better. you "wrap" a function by putting it as the only value in a list, and "map" pretty much acts as your function wrapper, but technically this you need to jump through a couple hoops to make it monadic, and I'm just not sure the metaphor is helpful here
A bigger issue is that you're missing a piece. If you can "wrap" values and "wrap" functions such that they operate on wrapped values, you (probably) have a functor. To be a monad you also need to have the ability to turn multiple layers of wrapping into one layer of wrapping. For lists, that's "flatten".
I said "probably" above because there are rules these pieces need to follow to behave well. They're pretty simple but I don't think we need to dig into them at this level of discussion.
(I'm trying very hard not to fall into the trying-to-explain-monads trap!)
I might have some inaccuracies in how I state this since I’m not from a functional programming background, but I think of monads as an abstraction of chaining functions over a value such that each returned value can specify what further transformations it supports, and particularly in such a way that generic transformations (like sequence reversal) can easily be applied, and errors or empty values can be accounted for within the control flow.
I think sometimes people call this a “fluent API”, but I would never call it “a generalization of callbacks” or of promises.
Anyway there’s a much more mathematically precise way of stating it but this is my intuitive caveman way of thinking about it.
Nan-in served tea. He poured his visitor’s cup full, and then kept on pouring.
The professor watched the overflow until he no longer could restrain himself. “It is overfull. No more will go in!”
“Like this cup,” Nan-in said, “you are full of your own opinions and speculations. How can I show you a Monad unless you first empty your cup?”
Example is best for illustration. I'll use a made-up syntax.
That's it! That's all to to monad. You can use the same pattern to build other monadic types, like List<T>, Promise<T>, IO<T>, as long as the wrap() and then() functions are built accordingly. Back to this example, to use it,I can’t even make stuff with fundamental groups.
1. I free solo a bunch of junk in vanilla javascript with state flowing hither and thither until I'm out of coffee
2. I test the exact behaviors(s) I wanted to make possible in the GUI I just wrote.
3. The framework whitelists only the event chains from my test.
4. For any blacklisted event chains, the user gets a Youtube video screencast of the whitelisted test so they can learn the correct usage of my GUI.
In that case, everything runs within the effect monad and then no one would ever really need to learn what a monad is, just that some calls are effectful (like reading a file or throwing an exception).
1 more comments available on Hacker News