What's a monad?
I've been learning OCaml (pretty much no experience prior). I have seen the term monad mentioned a few times, but I don't have any sense of what a monad is or how it is useful.
A quick search lead me to Wikipedia, which has the following unhelpful description:
In functional programming, a monad is a design pattern that allows structuring programs generically while automating away boilerplate code needed by the program logic. Monads achieve this by providing their own data type, which represents a specific form of computation, along with one procedure to wrap values of any basic type within the monad (yielding a monadic value) and another to compose functions that output monadic values (called monadic functions).
Maybe I'm too new to programming but this makes zero sense to me.
Posted Dec 11, 2019, 8:03 AM UTC · edited
Don't feel bad about not understanding that description - it's like describing driving as "a social pattern for moving people faster than horses".
One example monad is Maybe or Optional (named depending on your programming language). That's useful when you have values which are sometimes present and sometimes missing, but always of a certain type.
Optionals can contain a value, or be empty. Using a Java example because it's what I'm using at the moment, Optional.of(1) is an optional containing a value of 1. Optional.empty() is an empty optional.
If (say, in Java again) you are writing lots of code like `if (x == null) { y = null } else { y = x + 1}`, having to work around possibly missing values, you can rewrite to use the Optional monad instead - here you would do `y = x.map(n -> n + 1)`.
Using Optional in this case gives you type safety to avoid problems with null.
Another important thing about monads is that they can be combined. Say we have a function called `div(x, y)` which returns Optional<Int>. It returns Optional.of(x / y) most of the time, but returns Optional.empty() when dividing by 0.
`Optional.of(2).map(n -> div(n, 2))` returns `Optional.of(Optional.of(2))`
`Optional.of(2).map(n -> div(n, 0))` returns `Optional.of(Optional.empty())`
Here, it's a bit of a pain to get the result of the operation above, because it's "boxed" inside another optional. But all Monads have a flatMap operation, which allows you to sensibly "flatten" values.
`Optional.of(2).flatMap(n -> div(n, 2))` returns `Optional.of(2)`
`Optional.of(2).flatMap(n -> div(n, 0))` returns `Optional.empty()`
So a Monad is just a class of data types which are easily combined (with flatmap) and have a sensible "empty" value
This is really interesting to me as someone coming from a non-functional context. Where I'm from,
mapis a function/method that applies a function to each element of a collection. But here, it's being used to apply a function to a value that may not exist. What's the connection here?An Optional is a collection of length between 0 and 1 :)
Lists can count as monads too, if they define a flatmap. For a list, flatmap would look like this
`[1,2,3].flatMap(n => [n, n * -1])` returns `[1, -1, 2, -2, 3, -3]`.
In the context of lists, flatmap is just map + flatten
They are both the same map. What you refer to is map implemented for, say, lists just as it can be implemented for any other functor.
Personally I liked this explanation of monads
https://link.medium.com/2f5lR3ODk2
This looks promising. I'll read it tonight.
List.map [] f will apply a function to something that doesn't exist.
Most collections can be empty. option is just a collection that is empty or has one element.
Maybe my problem is that I don't understand maps and flatmaps
That's totally fair.
mapcomes to us from a "typeclass" calledApplicative. We don't talk about "typeclasses" much in OCaml, but you can loosely think of them as abstractly (think.mli) providing some sort of algebraic structure, and anytime you think "algebraic," think "obeying some set of laws." So forApplicative, we want to know two things:Applicativeprovide any other operations besidesmap?Applicativehave to obey?I think this post does a good job of answering these questions for
Applicative.flatmapcomes to us from a "typeclass" calledMonad, which is where this thread started. One thing to be aware of is that allMonads areApplicatives, so allMonads must providemapas well as the rest of the operations onApplicative, and satisfy theApplicativelaws. In addition, there is theflatmapoperation, and a couple of laws in addition to theApplicativelaws governing its behavior.By the way, all of this talk about "laws" can sound pretty intimidating, but we can loosely describe all of the laws as: "You must make sense." That is, the laws usually say things like "this typeclass has a notion of 'identity', and its operations on
xandidentitymust result inx" and "Given an operationopon valuesx,y, andz,x op (y op z)and(x op y) op zmust yield the same result." That is, you tend to have various kinds of "identity and "associativity" laws, and when you look at them, your immediate reaction is likely to be "Huh? It'd be completely bonkers for things to work any other way!"And you're right. And in the end, what you tend to find is you understand these things just fine. It's just that, when the smoke clears, you find that most languages and libraries violate them willy-nilly, and you just never realized it before. When you start demanding your tools not violate the grossest common mathematical sense, you end up with better, easier-to-understand code, albeit may not at first to colleagues who haven't yet learned to think in the same terms.
By the way, you'll find both
ApplicativeandMonadin Jane Street's "Core" library, if you haven't already seen them there.Why do you choose to explain
mapwithApplicativeinstead ofFunctor?mapis part of the definition of functor, and understanding applicative isn't /strictly/ necessary to understand monad.True. I tend to lump them together because both deal with effects in some context, with the
Applicativecontext not being shared, and theMonadcontext being shared.