Hacker News new | ask | show | jobs
by zug_zug 2508 days ago
I guess I don't get all this "monad" stuff. This article talks about 3 types of monad. An optional, a list, and a future.

However an optional is really just a list constrained to size 0 or 1. And a future is often called "not truly a monad."

So I question the value of explaining this abstraction in great detail over so many articles when people struggle to come up with more than 1 concrete example of it (Lists), an example that engineers have already understood since our first month coding.

Maybe somebody can speak to this more.

6 comments

Other replies talk about how Promises can be implemented as monads. Aside from that, you can come up with your own, too.

One monad that I occasionally use is something I'll call "Tracked". For "return" (when we make a new instance of the monad) we store a pair (initialValue, initialValue). For "bind" (when we act on what's in the monad) we only ever touch the second value in the pair, returning (initialValue, transformedValue).

That way, you can know where this piece of data came from. I've gotten a lot of mileage out of Tracked<Result<T>>: when one of your Results is an exception, then you can check what piece of data ended up triggering that exception. Yes, you could do this without the Tracked monad, but doing it monadically means that most of your functions don't need to know or care about tracking the initial data; you can just Apply those simpler functions and the Tracked instance will do it for you.

> And a future is often called "not truly a monad."

I think you've misunderstood this observation. It is possible to write a future library that gives a monad (I use such a library regularly). But the most common ones do not, because it happens to be a decent design decision in dynamically-typed languages to not quite obey the monad equations.

The next interesting monad is probably Haskell's Either (or OCaml's Result, if you're into that). It is only a slight twist on the optional monad. Where optional's None case contains no data, Either's Left case can contain data.

After the collections (list and optional), either, and future monads, the difficulty to understand useful monads without first understanding the category of monads jumps considerably. If you're interested, the next ones to look at would be the reader, writer, state, and continuation monads. There's also the classic example from Haskell, the IO monad.

You can go a very long way in programming without ever needing to explicitly use a monad for anything. The usual route into needing them is something like Haskell's IO: if you want as much as possible of your code to be stateless and immutable, how do you deal with IO, which inherently changes state external to the program?

If you've learned from the assembly end of programming upwards, it can be very hard to see the need for them at all.

You can go a very long way in programming without realising you inadvertently have been using Monad's present in the language/standard library :P
> However an optional is really just a list constrained to size 0 or 1.

True, but you've only written about them as data types, which misses the monadic part. What makes them monads is that you can join any Optional<Optional<T>> into an Optional<T>. Likewise you can join a List<List<T>> into a List<T>. It is that principled 'smooshing down' of the data types that makes them monads.

Promises, as exposited in the article, are an interface for bona fide monads. I think Promises are the best example of monads because they don't just wrap a type in memory. Maybe is kind of a subset of List (as you mention), and for both the contents are available in memory. Promises are not like Lists, and their contents can't just be pulled out, so you have to use `.then` (monadic bind) to do computation on them.
> However an optional is really just a list constrained to size 0 or 1.

What?

The sentence you quoted would make more sense if you interpret "is really just" like a mathematician, i.e. they're 'equivalent' in the sense that you can 'map' everything important about either in both directions.

More concretely, "an optional" is something that either 'has a value' or 'does NOT have a value'. So it "is really just a list constrained to size 0 or 1" in the sense that an optional that 'does NOT have a value' is equivalent to a list of size 0, i.e. a list with no contents/members, and an optional that DOES 'have a value' is equivalent to a list of size 1, i.e. it's 'value' is the single/unique member of the list.

Think of statements like 'is really just' in the sense that an integer between 0 and 255 'is really just' 8 ordered bits – they're 'equivalent' in the sense that you can map all possible values of those integers to all possible values of 8 ordered bits, and vice versa, and (importantly in a mathematical sense) in a way such that every integer is mapped to a single set of 8 ordered bit values and every set of 8 ordered bit values is mapped to a single integer. In mathematics that's often described as an 'isomorphism' which is, in working programmer terminology, just a way to convert back and forth between two sets of values 'perfectly and losslessly'.

An empty list (length zero) is "None" and a single element list is "Some" or "Just" whatever the lone element in the list is.

And of course the list is constrained to not contain more than a single entry.

He means that the type `Optional a` is isomorphic to the type `ListOfLengthAtMostOne a`. This is because we can pair their values up perfectly: Nothing ~ [] and Just a ~ [a].