Created
July 28, 2011 01:12
-
-
Save cqfd/1110731 to your computer and use it in GitHub Desktop.
Monads, explained with Ascii art.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Monads are composed of three ingredients. | |
First, for a given monad m, there are monadic values of type m a, for some type | |
variable a. In Haskell, if a variable x has type t, we write x :: t. | |
Here's a picture of a monadic value of type m a. | |
+--------\ | |
| | \ | |
| m | a > :: m a | |
| | / | |
+--------/ | |
Picture it as a box, labelled by m, with a triangle on the side, labelled by a. | |
The second ingredient is a function, which maps an argument of type a to a new | |
monadic value of type m b, where b is another type variable. Sometimes b will be | |
equal to a, but it needn't be. | |
.\---------\ | |
. \ | \ | |
. a > m | b > :: a -> m b | |
. / | / | |
./---------/ | |
The diagram shows that if you were to stick an a into the left-hand side of the | |
picture, you'd get a m b box. You can think of this second ingredient as being | |
a monadic callback; given an a, you can use it to produce an m b. | |
The third ingredient in a monad is a way to combine an m a boxe with an | |
a -> m b box, such that you get another monadic box. This third ingredient is | |
another function. It's type is m a -> (a -> m b) -> m b. If you give it an m a | |
and a function of type (a -> m b), it will give you an m b. | |
Here's what happens if you feed an m a into an (a -> m b): | |
+--------\---------\ | |
| | \ | \ | |
| m | a > m | b > | |
| | / | / | |
+--------/---------/ | |
From far away, this looks like an m b: | |
+--------\ | |
| | \ | |
| m | b > :: m b | |
| | / | |
+--------/ | |
Even if you know nothing else about monads, your inner computer scientist should | |
enjoy these diagrams. These little boxes are composable: you can take a little | |
m a box, combine it with an (a -> m b) box, and get another box, this time | |
with type m b. You could then combine this new box with a (b -> m c) box, | |
and get back an m c box. We're combining building blocks and getting bigger building | |
blocks back. Awesome. | |
This idea, of composing building blocks together to get new building blocks, is one | |
of the fundamental ideas of computer science, and it's what monads are all about. | |
** The monad laws | |
There are actually a few other ingredients to monads, called the monad laws. There | |
are three of them. They're easy and they fall right out of the pictures. | |
First, suppose you have a | |
.\---------\ | |
. \ | \ | |
. a > m | b > :: a -> m b | |
. / | / | |
./---------/ | |
and you also have a | |
.\---------\ | |
. \ | \ | |
. b > m | c > :: b -> m c | |
. / | / | |
./---------/ | |
You can combine these guys to get one of these | |
.\---------\---------\ | |
. \ | \ | \ | |
. a > m | b > m | c > :: a -> m c | |
. / | / | / | |
./---------/---------/ | |
One of the monad laws says that if you combine three such callbacks, it | |
doesn't matter how you parenthesize the combining. You can combine the first two, | |
and then add on the third, or you can combine the last two | |
and then add the first one in front. The combining is associative. At the end | |
of the day, no matter how you parenthesize, you get the same callback: | |
.\---------\---------\---------\ | |
. \ | \ | \ | \ | |
. a > m | b > m | c > m | d > :: a -> m d | |
. / | / | / | / | |
./---------/---------/---------/ | |
The next two monad laws state that for any monad m, there exists a funny | |
box that looks kind of like this: | |
.\--\ | |
. \ \ | |
. a > a > | |
. / / | |
./--/ | |
That is, there exists a function of type (a -> m a) for any type a that is as | |
thin as possible. Combining it with a | |
.\---------\ | |
. \ | \ | |
. a > m | b > | |
. / | / | |
./---------/ | |
on the left gives | |
.\\---------\ | |
. \\ | \ | |
. a >> m | b > | |
. // | / | |
.//---------/ | |
One of the monad laws says that this equals | |
.\---------\ | |
. \ | \ | |
. a > m | b > | |
. / | / | |
./---------/ | |
which is the thing we started with. Likewise if you add it on the right: | |
.\---------\\--\ | |
. \ | \\ \ | |
. a > m | b >> b > | |
. / | // / | |
./---------//--/ | |
The final monad law says this is just what we started with: | |
.\---------\ | |
. \ | \ | |
. a > m | b > | |
. / | / | |
./---------/ | |
** Takeaways | |
Whenever you want to understand a particular monad, you need to understand how | |
to combine monadic values of type m a | |
+--------\ | |
| | \ | |
| m | a > :: m a | |
| | / | |
+--------/ | |
with a monadic callback, | |
.\---------\ | |
. \ | \ | |
. a > m | b > | |
. / | / | |
./---------/ | |
to get a new monadic value of type m b, | |
+--------\---------\ | |
| | \ | \ | |
| m | a > m | b > | |
| | / | / | |
+--------/---------/ | |
Each monad implements this means of combination in its own way, but the basic | |
idea is always the same. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment