January 7, 2022

Understanding Monads

This is a challenge for many people, it certainly was for me. I don't know if I will wind up writing yet another Monad tutorial, but I do want to offer some tips.

First of all, don't let this unfamiliar word scare you. If you are a mathematician and familiar with "category theory" you may have encountered this word and concept before, but if so, you are one out of a million people. I mention this, not because I have any experience with "category theory", but just so you know where this odd word "monad" comes from.

Many people understand and use monads, and so can you.

What is most important to learn about monads is that they are all about a certain sort of function composition. (I.e. chaing of operations.) Some special operators have been developed (three of them by my count) that provide the glue that makes all this work, but we are starting to get ahead of ourselves.

In other words monads are about plumbing. One person called a monad a "type disciplined pipeline". So the monad is about the ability to chain functions together, not about what is being chained together.

Forget about IO. Monads are not about IO. Tackling Monads and IO at the same time will just confuse you. Indeed IO is a monad, but the whole pure/impure world issue is independent of (orthogonal to) the Monad business, and tackling both of these at the same time will just confuse you. Learn monads, then go back to IO later.

It seems to be true that monads deal with "fancy values". So we have a value with some added side property, and this business of monad chaining disciplines the way this side property gets propogated.

Most importantly, "Monad" is a type class. If this is new to you, you should go study that and get a clear understanding before trying to digest anything further. Many Haskell data types belong to the Monad type class, and it may not be necessary or helpful in most cases to worry about them being Monads. Lists are an example.

For some time I thought monads were all about IO, but this is most definitely not the case. Monads are indeed used to handle IO in Haskell, but you can (and should) get a handle on monads without even thinking about Haskell IO.

Haskell "do notation" is all about making this monad chaining business more pretty. Since we are talking about a sort of pipeline, the fact that "do notation" yields a sequencing of operations should not be a surprise.

What a type class does is to specify certain behaviors and operators that a data type must support. There is an Eq type class, and for the == and != operators to make sense, a data type must belong to this type class.

In general, Monads "wrap" or augment a basic data type with a certain "smell" or character. A list Monad for example can have a plurality of values. The "Maybe" Monad augments a basic data type with the possibility of failure. The key aspect in dealing with Monads is to maintain and propagate that augmented character in a way that is elegant and convenient for the programmer.

To understand any type class, the heart of the issue is to understand the operators that the class supports. Here they are for Monads. There are just three that you need to worry about.

return

The function "return a" promotes a value "a" into the monad in question. For example with a list, we end up with [a]. With the Maybe Monad, we get "Just a".

bind

This is symbolized by (>>=) It is used to pass a Monadic value "down the line" in a sequence of computations. It takes a Monadic value, extracts the value and passes that to some function that expects an ordinary value, but once again gives us a Monadic value.

and then ...

>>

This is symbolized by (>>). Yep, two "greater than" signs (bind without the trailing "equal sign"). This is like "bind" but does not extract the value from the Monad, it just passes a monadic value along to something next in line that expects a monadic value. It is less commonly used than bind.

"do" notation

This is a special little sublanguage (a DSL or "domain specific language") that eliminates all the hieroglyphics and makes dealing with monads look a lot like good old imperative programming. A few people look down their noses at it, but most realize that in some cases it makes for clearer and more readable code and is a benefit. I should have a description of this for you elsewhere. It is commonly used for IO, but by no means restricted to it.

The "IO" monad

This also is its own special topic, but heavily invested into the world of Monads. However, it is important to realize that Monads are not just about IO. IO is just one important case where Monads are used.

References

There are countless inscrutable monad tutorials online. The following are some selected and valuable discussions that are worth reading. The following essay works up Monads using javascript. It is very much worth reading carefully as it develops the key concepts of monads with very little reference to Haskell. It is a nice transcription of the second article "you could have invented monads".
Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org