Top
Best
New

Posted by polygot 6/29/2025

List is a monad(alexyorke.github.io)
157 points | 179 commentspage 3
danieltanfh95 7/3/2025|
The obsession with trying to explain a monad ultimately stems from conflicting explanations and the inability to differentiate between a mathematical monad and monads implemented in software.

Monads in software are just a standard API for any given type. That’s it. Theres no magic here. Just implement the standard and you have a monad.

It grinds my gears seeing monad tutorial after tutorial using the wrong metaphors or misleading explanations

petesergeant 7/3/2025||
> Monads in software are just a standard API for any given type. That’s it. Theres no magic here.

I don’t think that’s helpful for people to understand _why_ monads though, and that’s generally what people are looking for.

danieltanfh95 7/14/2025||
a standard api is the magic. when you can talk to anything via a standard api it creates uniformity and standard blocks you can build from
lmm 7/3/2025||
Associativity is important, and not something that can be expressed in the API in most languages.
danieltanfh95 7/14/2025||
Associativity is part of the contract/specifications of said "standard API"
kazinator 7/3/2025||
https://www.kylheku.com/cgit/lisp-snippets/tree/monads.lisp

[Content Warning: Lisp]

We define a small OOP framework for monads, plus macros which then can succinctly define different kinds of monads, including generating a comprehension macro for each one.

Smaug123 7/2/2025||
The article sort of danced around what I think is the most natural way List is a "recipe": it's the bounded nondeterminism monad (a `List<T>` is a nondeterministic result; one could implement `List<T> -> T` by selecting an answer uniformly at random from the finite multiset).
perlgeek 7/3/2025|
I really like my lists to be deterministic.

Seriously, I've read things about lists and nondeterminism a few times in this thread, and I can't help but wonder if "you guys" (functional programming nerds, maybe?) use the word "nondeterministic" different than the rest of the world?

If not, I'd love a good explanation about what makes lists non-deterministic, and why we would want that, and why they seem to be perfectly deterministic in imperative programming languages.

housecarpenter 7/3/2025||
It is a particular sense of "nondeterminism", but it's not specific to functional programming, I think it's the usual one theoretical CS as a whole. It's the same sense in which "nondeterminism" is used in P vs NP, for example.

Think of a computation as a process of changing state. At a given point in time, the computer is in a certain state, the current state. The computation can be described in terms of a function that acts on the current state.

In a deterministic computation, the function takes in the current state, and produces a single state as output which will be the state the computer enters on the next tick.

In a non-deterministic computation, the function takes in the current state and produces a set of states as output. These states are all the possible states the computer might enter on the next tick. We don't know (or just don't care) which one of these states it will enter.

You can model a non-deterministic computation as a deterministic one, by using a list `currentStates` to store the set of all possible current states of the computation. At each "tick", you do `currentStates = flatMap(nextStates, currentStates)` to "progress" the computation. In the end `currentStates` will be the set of all possible end states (and you could do some further processing to choose a specific end state, e.g. at random, if you wish, but you could also just work with the set of end states as a whole).

It's in this sense that "a list is a non-deterministic result", although this is really just one thing a list can represent; a list is a generic data structure which can represent all sorts of things, one of which is a non-deterministic result.

tel 7/2/2025||
Monad tutorials are on the rise again.

Let's start with function composition. We know that for any two types A and B we can consider functions from A to B, written A -> B. We can also compose them, the heart of sequentiality. If f: A -> B and g: B -> C then we might write (f;g) or (g . f) as two different, equivalent syntaxes for doing one thing and then the other, f and then g.

I'll posit this is an extremely fundamental idea of "sequence". Sure something like [a, b, c] is also a sequence, but (f;g) really shows us the idea of piping, of one operation following the first. This is because of how composition is only defined for things with compatible input and output types. It's a little implicit promise that we're feeding the output of f into g, not just putting them side-by-side on the shelf to admire.

Anyway, we characterize composition in two ways. First, we want to be clear that composition only cares about the order that the pipes are plugged together, not how you assemble them. Specifically, for three functions, f: A->B, g: B->C, h: C->D, (f;g);h = f;(g;h). The parentheses don't matter.

Second, we know that for any type A there's the "do nothing" identity function id_A: A->A. This doesn't have to exist, but it does and it's useful. It helps us characterize composition again by saying that f;id = id;f = f. If you're playing along by metaphor to lists, id is the empty list.

Together, composition and identity and the rules of associativity (parentheses don't matter) and how we can omit identity really serve to show what the idea of "sequences of pipes" mean. This is a super popular structure (technically, a category) and whenever you see it you can get a large intuition that some kind of sequencing might be happening.

Now, let's consider a slightly different sort of function. Given any type types, what about the functions A -> F B for some fixed other type F. F here exists to somehow "modulate" B, annotate it with additional meaning. Having a value of F B is kind of like having a value of type B, but maybe seen through some kind of lens.

Presumably, we care about that particular sort of lens and you can go look up dozens of useful choices of F later, but for now we can just focus on how functions A -> F B sort of still look like little machines that we might want to pipe together. Maybe we'd like there to be composition and identity here as well.

It should be obvious that we can't use identity or composition from normal function spaces. They don't type-check (id_A: A -> A, not A -> F A) and they don't semantically make sense (we don't offhand have a way to get Bs out of an F B, which would be the obvious way to "pipe" the result onward in composition).

But let's say that for some type constructors F, they did make sense. We'd have for any type A a function pure_A: A -> F A as well as a kind of composition such that f: A -> F B and g: B -> F C become f >=> g : A -> F C. These operations might only exist for some kinds of F, but whenever they do exist we'd again capture this very primal form of sequencing that we had with functions above.

We'd again capture the idea of little A -> F B machines which can be plugged into one another as long as their input and output types align and built into larger and larger sequences of piped machines. It's a very pleasant kind of structure, easy to work with.

And those F which support these operations (and follow the associativity and identity rules) are exactly the things we call monads. They're type constructors which allow for sequential piping very similar to how we can compose normal functions.

HexDecOctBin 7/3/2025|
Thank you so very much. This is the first time monads have made sense to me, and now its clear why. People who try to explain them usually end up adding all kinds of Haskell minutia (the top post is another example), rather than actually explain the concept and why we need it. Your comment is the first time I actually understand what it is, and why it might be useful.
mvdtnz 7/2/2025||
The amount of people who tie themselves into knots to understand this pointless concept is very funny to me. I am 16 years into a successful software engineering career without learning what a monad is an it never held me back. Turns out I can use lists and optional types and all that jazz without it.

I mean really. Look at posts like this[0]. What does this give you? Nothing, in practical reality. Nothing.

[0] https://news.ycombinator.com/item?id=44446472

lmm 7/3/2025||
> I am 16 years into a successful software engineering career without learning what a monad is an it never held me back.

How would you know? That's the classic Blub Paradox.

Being able to write a custom monad and then leverage the vast array of libraries that already exist has helped me deliver functionality to end users quicker, more maintainably, and with lower defect rates. They don't let you do anything that you couldn't do by writing it out longhand. But just like using generic container libraries instead of writing a specific container for every type you want to handle collections of, they're extremely helpful.

battle-racket 7/2/2025||
funny that you call it pointless then admit you never learned what it is
daxfohl 7/2/2025||
Nit, in Haskell it's a MonadPlus. Which IIRC is a monad that supports filtering.
ChadNauseam 7/2/2025|
In Haskell List is a Monad as well as MonadPlus. Since List's Monad instance is used probably 100x more than its MonadPlus instance, I think it makes sense to focus on that,
nyeah 7/2/2025||
Let's keep up the mathiness. Look how much economics has benefitted from that.
agnishom 7/3/2025||
Somebody is keeping a giant list of Monad tutorials, right?
lordgilman 7/3/2025|
Yes, and we are approaching peak monad tutorial https://wiki.haskell.org/Monad_tutorials_timeline
throwaway290 7/3/2025||
So in the golden age of the internet we had the highest rate of monad tutorial growth. Churn them out, bring back the good times
revskill 7/2/2025||
U must prove it is a monoid in the category of endofuncors.
rebeccaskinner 7/2/2025||
join has the type `m (m a) -> m a`. That's the thing that really shows off the monoidal structure. People normally implement monads in terms of bind, but you can easily define join in terms of bind for any Monad: `join ma = ma >>= id`. So really, as long as you have a lawful instance of Monad written with bind the existence of join is your proof.
hirvi74 7/2/2025||
> monoid in the category of endofuncors.

I do not even know what a monoid or an endofuncor is. While I enjoy math, despite not being the best at it, I am confident I never made it this far in my studies. I looked at the Wikipedia definitions, and I am even more confused now.

1-more 7/2/2025||
https://bartoszmilewski.com/2016/12/27/monads-categorically/

This is a book chapter, and you need the preceding chapters to grasp it I think. I'm still in the middle of it.

draw_down 7/2/2025|
[dead]
More comments...