Monads and why do they matter

Yurii Gorbylov
4 min readFeb 22, 2018

This article aims to explain the purpose of monads in functional programming. Yeah, I know, there are tons of information on this subject in various blogs and books, but almost all of them are either too complex with involving category theory and boring formulas explanation or too superficial with describing just main concepts.

So, this is my attempt to show that monads are not just mathematical constructs from category theory, but powerful abstractions to solve real-life problems.

Introducing Monads

We should start with a point that Monad is a design pattern in functional programming. Just like Singleton, Factory, Observer and other well-known patterns from OOP it solves some problem. To get a better understanding what kind of problems it solves, let’s consider a chronology that leads us to monads using.

1. In functional languages, we operate via functions. Let’s define two functions: f and g.

2. Now, to write a program we want to execute these functions in specific order. To do it we can compose functions.

3. Function f might fail (i.e. f(0)).

Thrown exception is a side effect that can lead to unexpected behavior of the program and should be explicitly handled in all of the places that an exception might occur. This is not the way how we do it in functional languages. So, how do we solve it?

4. Let’s create a new type of data to be returned. A container that represents a computation that may either return a successfully computed value or error. Hence, we can have:

5. But function g takes a Double and is not ready to consume a Try[Double].

6. Solution: a special function flatMap to compose sequence of Tries.

We just described monad Try(*). It encapsulates an effect — computation that may either result in an exception or return a successfully computed value. This is what Monad does — wraps a value and gives it some effect. That’s it. You just take a value, put it into a monad and get back an amplified value. An “amplified” means that the power of value’s type is increased.

There are other kinds of effects. Option is a monad that gives to wrapped type an effect denoting whether a value is missing. List is another monad — represents a computation which may return an arbitrary number of results. Future — gives you a free asynchronous computation of wrapped value.

(*) Actually, Try fails the left identity monad law. Since this article is mostly for beginners, let’s leave this moment.

Monad functions

Each monad has two functions:

  • unit
  • flatMap

Scala doesn’t provide a Monad type like other functional languages does, but, as we mentioned, Monad is not a specific class, it is a concept and we can easily define own trait with functions unit and flatMap.

The unit stands for a single argument constructor of a monad. You give it a value of type A, and it generates you a Monad[A] in return. We define unit out of the trait because there is no sense to invoke it from already existing monad instance (e.g. tryF.unit(1/x)). We want it as a standalone static method (e.g. Try.unit(1/x)).

The flatMap does the flatting/composing part. It knows how to handle a function which itself returns a value of the monad type. Let’s consider an example.

You could already use List in Scala collections. Let’s say we have a list of strings:

Now we want to split every world in the list to chars:

Clearly this returns a list of lists: List[List[Char]]. Sometimes this is what we want, but very often we actually want to get a single flatten list so that, for example, we can iterate over all of the elements. This pattern turned out to be so common that the act of mapping and then flattening is considered to be a basic operation of Monad.

So, what the flatMap does is that it takes a function with signature String → List[Char] and flatten our List[List[Char]] into List[Char].

Scala’s for-comprehension (syntax sugar over map, flatMap, filter functions) well illustrates how monads can be chained together in specific order.

Monad laws

There are three laws of monads, namely the left identity, right identity, and associativity. So, every type that has functions unit and flatMap and claims to be a monad must obey the laws. If not — it won’t behave like a monad and might do some unexpected things.

1. Left identity

2. Right identity

3. Associativity

The two identity laws basically say that unit function doesn’t change the value in a monad.

Associativity law says that when we have a chain of flatMap functions it shouldn’t matter how they’re nested.

Conclusion

Monad is a simple and powerful design pattern for function composition that helps us to solve very common IT problems such as input/output, exception handling, parsing, concurrency and other. Application becomes less error prone. Code becomes reusable and more readable. And Haskell freaks may even notice you around.

--

--