←back to thread

A list is a monad

(alexyorke.github.io)
153 points polygot | 2 comments | | HN request time: 0s | source
Show context
brooke2k ◴[] No.44445948[source]
As far as monad tutorials go, this one seems quite good. I like the categorization of monads between "containers" and "recipes".

However, I personally think that monad tutorials tend to give people the wrong impression and leave them more confused than they were before, because they focus on the wrong thing.

A monad is not a complex concept, at all. IMO a more useful way to present the topic would be with one separate lesson for every common monad instance. Start with Maybe, then IO, then maybe State and List, and so on... because ultimately, every instance of a Monad works very differently. That's why the pattern is so useful in the first place, because it applies to so many places. (Note: this is a criticism of monad tutorials in general, not this one in particular, which seems to do a decent job on this front).

In my experience, people new to Haskell focus way too much on getting the "a-ha" moment for monads in general, when really you want a bunch of separate "a-ha" moments as you realize how each instance of a monad takes advantage of the pattern differently.

I also tend to think that monads are best demonstrated in Haskell rather than in other languages, if only because the notation is so much less clunky. That may just be me though. (EDIT: well, also because almost no other languages have typeclasses, so you have to approximate it with interfaces/traits/etc)

Also FYI: in part 2, the code examples have extra newlines in between every line, which makes it hard to read (I'm on firefox, if that matters).

replies(15): >>44446327 #>>44446377 #>>44446564 #>>44446988 #>>44447713 #>>44448118 #>>44448413 #>>44449093 #>>44449627 #>>44449895 #>>44450873 #>>44450887 #>>44451012 #>>44451851 #>>44458827 #
pdhborges ◴[] No.44446327[source]
If all monad instances work differently what is the value of the Monad interface? What kind of usefull generic code can one write against the Monad interface.

Related: https://buttondown.com/j2kun/archive/weak-and-strong-algebra...

replies(11): >>44446453 #>>44446472 #>>44446556 #>>44446586 #>>44446781 #>>44446882 #>>44447360 #>>44448151 #>>44448170 #>>44450818 #>>44462125 #
ChadNauseam ◴[] No.44446453[source]
Lots of useful generic code. MapM is a version of `map` that works with any Monad, `sequence` works with any monad, and so on. These are used very frequently.

But the bigger benefit is when syntax sugar like `do` notation comes in. Because it works for any Monad, people can write their own Monads and take advantage of the syntax sugar. That leads to an explosion of creativity unavailable to languages who "lock down" their syntax sugar to just what the language designers intended. In other words, what requires a change to other languages can often be a library in Haskell.

replies(1): >>44447334 #
bjourne ◴[] No.44447334{3}[source]
What can a Haskell monad do that a Python class cannot? 99% of all monads I've seen only facilitate local state manipulation.
replies(4): >>44447669 #>>44447962 #>>44448127 #>>44454006 #
AnimalMuppet ◴[] No.44447962{4}[source]
This may be a dissenting opinion, but... Haskell tried to avoid mutable state. "Local state manipulation" was not really a thing you could do in Haskell, deliberately. Then someone figured out that you could (ab)use a monad to do that. And because that was the only way, whenever they needed to manipulate state, Haskell programmers reached for a monad.

So it's not "what can a Haskell monad do that a Python class cannot". It's "what can a Python class do in a straightforward way that Haskell has to use a monad for, because Haskell put the programmer in a straightjacket where they couldn't do it without a monad". It's basically a pattern to get around the limitations of a language (at least when it's used for state).

replies(2): >>44448201 #>>44448281 #
tome ◴[] No.44448201{5}[source]
This is not historically how Haskell was developed. Haskell didn't try to "avoid mutable state". Haskell tried to be (and indeed succeeded in being) referentially transparent. Now, it turns out that you can't uphold referential transparency whilst having access to mutable state in the "traditional" way, but you can access mutable state if you introduce monads as a means of structuring your computation.

So, they're certainly not a means of getting around a limitation of the language. If it was just a limitation that limitation would have been lifted a long time ago! It's a means of preserving a desirable property of the language (referential transparency) whilst also preserving access to mutable state, exceptions, I/O, and all sorts of other things one expects in a normal language.

See my comment here for a little bit more about the benefits of referential transparency: https://news.ycombinator.com/item?id=44448127

replies(1): >>44448429 #
AnimalMuppet ◴[] No.44448429{6}[source]
But historically, wasn't there a fair period of time between Haskell insisting on referential transparency (and therefore not allowing traditional mutable state) and monads being introduced as a way to deal with it? That was my understanding of the history.

And if so, then it seems fair to say at least that monads were a way to get around the limitations imposed by a desirable feature of the language...

replies(1): >>44448683 #
1. tome ◴[] No.44448683{7}[source]
> But historically, wasn't there a fair period of time between Haskell insisting on referential transparency (and therefore not allowing traditional mutable state) and monads being introduced as a way to deal with it? That was my understanding of the history.

Yes, although there were solutions in the meantime. I/O was performed in the original version of Haskell through input-output streams and continuation passing style. It turns out that both approaches could have been given monad interfaces if "monad" as an abstraction had been understood at the time, but it wasn't, so they had ad hoc interfaces instead.

> And if so, then it seems fair to say at least that monads were a way to get around the limitations imposed by a desirable feature of the language...

I mean, sort of, but that seems more of a judgement than a fact. Would you say that function calls in C were a way to "get around the limitations imposed by not allowing global jumps"?

In both cases I'd just say they're a useful abstraction that lets you achieve a well-specified goal whilst preserving some desirable language property.

replies(1): >>44451244 #
2. Dylan16807 ◴[] No.44451244[source]
> Would you say that function calls in C were a way to "get around the limitations imposed by not allowing global jumps"?

If C had started with a rule against global jumps, and only figured out function calls later, then yes I would say that.