# For the Love of Haskell and Monads

This post aims to explain the concept of monads while highlighting Haskell's elegance. You do not need to know the Haskell language to read this post.

Let’s first start with the beginning and discuss the concept of functors.

# Functors

This is a closed box:

Of course, as with every closed box, you can’t really know what’s inside unless you open it. So let’s open it, and… surprise!

The box contains the answer to the Ultimate Question of Life, The Universe, and Everything (who could have thought with such a small box?).

Yet, sometimes a box can also be empty:

How do we map the box analogy to programming? A box can be any data structure surrounded by a context. Said differently, a wrapper or container type that adds additional information or behavior to the underlying data.

In this example, the blue box represents the possibility of an optional value, which In Haskell is denoted by the `Maybe`

type. But the concept exists in many languages: `Option`

in Rust, `sql.NullString`

in Go, `Optional`

in Java, etc.

Yet, other type of boxes also exist:

- A box representing that the wrapped value can be either from one type or another. In Haskell, the
`Either`

type represents a value that is either`Left`

or`Right`

. In Rust,`Result`

represents either a success or an error, etc. - More generally, most
*classic*data structures we can think of such as a list, a map, a tree, or even a string. Those data structures can contain zero, one, or multiple values inside. For example, a string can be composed of zero, one, or multiple characters.

We know how to apply a function to a simple value; for example, applying a function that adds one to a given int:

But what if we want to apply the same function to a value inside of a box? We could open the box, extract the value out of it, apply the function, and put the result back in a box:

Yet, in Haskell, we can use `fmap`

to apply a function on a box directly; no need to perform all the steps ourselves:

`fmap`

is used to apply a transformation function, here (+1), to the value inside of a box and put the result inside another box.

In this example, the box itself is called a **functor**. A functor is an abstraction that allows for mapping a function over values inside a context **without** altering the context itself.

Not altering the context is crucial; a functor is not similar to the box in Schrödinger’s experiment. In quantum physics, opening a box alters the state of what’s inside. Here this is not the case: the box with the value 42 remains identical. And that’s a good thing, right? The answer to life shouldn’t be changed just by looking at it.

Examine the Haskell code for this example:

If you’re unfamiliar with Haskell, let’s spend 30 seconds on this snippet. The first line defines the signature of `fmapEx`

: a function that takes no input and produces a `Maybe Int`

output. The second line represents the core logic of the function: applying the (+1) transformation function to `Just 42`

(`Maybe`

is a box type, while `Just 42`

is an instance of this box with the value 42 inside).

As we said, a box can either contain a value or be empty, so what happens if we apply the (+1) transformation to an empty box? The result is also an empty box:

Applying (+1) to a non-existent value doesn’t give 1. You can’t increase your bank account balance if you don’t have a bank account; the same is true in Haskell.

Here’s the code for this new example:

`Nothing`

means a `Maybe`

box with no value inside.

We also said that the box analogy can be applied to other data structures; for example, a list of values. Therefore, we can reuse the same logic and apply the (+1) function to every list’s element:

Pretty handy, right? Instead of looping manually over each element and creating a new list, we can provide a transformation function to `fmap`

and Haskell will handle the rest for us.

That’s the essence of functors: an abstraction representing something to which we can apply a function to the value(s) inside.

Yet, in the next section, we will see that functors are somewhat limited. Let’s now move on to applicatives.

# Applicatives

What if instead of applying a transformation function to a box like this:

We wanted to apply a transformation function which was itself **inside** of a box:

In that case, using `fmap`

and functors, that’s a compilation error:

Indeed, the `fmap`

function only works if the transformation function is **outside** of any box.

But hold on… What’s the purpose of having a transformation function inside a box?

A few examples:

- When we want to represent a situation in which a function is optional or may be absent and that we need to handle this possibility explicitly using
`Maybe`

. - When we want to handle a variable number of functions, we can put these functions in a list.

Now that we understand why functions inside boxes are a thing, let’s understand how to handle this case:

The solution is to switch to another type: **applicative functors** (also called applicatives).

In this case, we must use in Haskell the `<*>`

operator with applicatives (sorry if it sounds cryptic):

Thanks to the `<*>`

operator, we can now apply the (+1) function inside a `Maybe`

applicative to the value inside another `Maybe`

applicative.

A small note, have you noticed that we referred to `<*>`

as an **operator**? In Haskell, an operator is also a function, but written in infix notation; meaning placed between its arguments:

Now what happens if we try to use `<*>`

on two different applicative types? For example, a `Maybe Int`

and a list of `Int`

:

In this case, that’s a compilation error. Applicatives are also there for safety reasons; the context has to be the same to use the `<*>`

operator. Yet, if the transformation function is inside a list as well, this time it works:

And do you want to know what happens if we have multiple transformation functions in the first box and multiple values in the second box? Haskell applies the combination of each transformation function on each value:

So that’s what an applicative is: another abstraction that allows for applying functions wrapped in a context to values in the **same context**.

One last thing, what if a transformation function remains outside of a box?

Should we put this function inside a box? Should we turn the applicative into a functor to apply `fmap`

? None of these is required. We can use the `<$>`

operator, basically the `fmap`

version for applicatives:

It illustrates that an applicative is an **extension** of a functor as it can cover both cases:

- If the function is
**outside**a box, we use`<$>`

(`A`

and`B`

being generic types):

- And if the function is
**inside**a box, we use`<*>`

:

Yet, same as with functors, we will also see that there’s a limit to how applicatives are helpful. Now, it’s time to move on to the final boss: monads.

# Monads

So far, we have tackled two kinds of transformation functions:

- A function outside of any box:

- A function inside of a box:

But what if a function takes a value outside a box, applies a transformation, and puts the result inside a box?

First, let’s talk about the how, as there are two ways to do it in Haskell. We can either use `Just`

as we want to return a `Maybe Int`

:

This function takes an `x`

variable outside a box and puts the sum of `x`

+ 1 inside a `Maybe Int`

box. But there’s a second alternative that does exactly the same thing, this time using `return`

:

`return`

is a function that wraps something (an int, a function, whatever) inside of a box. Thanks to type inference and the function signature, Haskell knows that `return`

applied on `(x + 1)`

should put this value inside a `Maybe Int`

. We will come back later to the essence of what `return`

is in Haskell.

Now, let’s move on to the why. Why does a function accept a value outside a box and return a value inside a box? For example, consider the case of a safe `divide`

function:

This function accepts two `Float`

, `x`

and `y`

, and returns a `Maybe Float`

. Then, it uses pattern matching:

- If the denominator is 0, it returns
`Nothing`

- Otherwise, it returns the result of
`x`

/`y`

inside a`Maybe Float`

box

`divide`

illustrates a function accepting inputs outside any box and returning a value inside a box.

Now let’s get back to our initial problem, can an applicative work with a function returning a value inside a box? Let’s give it a try.

## The Limitation of Applicatives

We will cover a concrete scenario. We want to implement a function that receives a person's age and name. We want to greet the person only if he’s over 18 years old. For example:

- Providing “
*John*” and 30 to our function should return a`Maybe String`

box with “*Hello John*”:

- Yet, with the age of 16, for example, is should return
`Nothing`

:

Let’s first introduce the two utility functions to validate the age and greet the person:

`validateAge`

uses Haskell’s guards syntax (`|`

), a notation to define functions based on predicate values:

- If the age is above 18, we put it inside a
`Maybe Int`

- Otherwise, we return
`Nothing`

Regarding the `greet`

function, it concatenates “*Hello *” and the person’s name (using the `++`

operator).

Back to applicatives, one could be tempted to write it this way:

Yet, this code doesn’t even compile. Let’s understand why.

The first part of the expression (`greet <$> (Just name)`

) is OK, as we have seen that the `<$>`

operator can be used to apply a function outside of a box to the value inside of a box:

This part of the expression takes a `String`

and produces a `Just String`

:

The second part of the expression, `validateAge age`

, is a `Maybe Int`

:

Now, taking the whole expression, it gives us the following:

And this part doesn’t compile. Indeed, we discussed previously what kind of function is expected by the `<*>`

operator:

In this case, we can’t provide the correct type expected by `<*>`

for the transformation function. So, we can’t make it work with applicatives (at least easily). We need something else.

## Monads to the Rescue

To solve our problem, we can use **monads** and introduce a new operator, `>>=`

:

This operator (still a function written in infix notation) takes:

- A value of type
`A`

inside a box - A function that transforms an
`A`

type into a`B`

type inside a box

As a result, it produces a `B`

type inside a box. For instance:

This example in Haskell:

This is the first use case of monads, applying a transformation function that returns a value inside a context to a value inside the same context type.

Now, let’s return to our problem (greeting if a person is over 18) and understand how to solve it using monads and the `>>=`

operator:

Note that the code here uses a **lambda** function. A lambda in Haskell is an anonymous function using the `\`

notation. For example, `\x -> x + 1`

, which increments its input `x`

by 1. In the previous code, the lambda represents a function that takes an `Int`

(because `validateAge age`

is a `Maybe Int`

) and returns a `Maybe String`

.

This is what our code looks like:

There’s one small thing that we could be bothered about. The transformation function passed to `>>=`

takes an `Int`

but **doesn’t use it**. This is the purpose of `_`

, to express that we want to ignore this parameter. Could we do better? Yes!

To solve the same problem without a *clumsy* lambda expression that doesn’t even use its input, we can use the `do`

notation:

The behavior of this code is the same as the previous one when we used the `>>=`

operator:

- If
`validateAge age`

returns`Nothing`

(age under 18), the whole function returns`Nothing`

. The computation terminates line 3 without further evaluation. - Otherwise, it returns a string inside a
`Maybe`

.

Using the `do`

notation, monadic expressions are written line by line. It may look like imperative code, but they’re just sequential, as each value in each line relies on the result of the previous ones and their contexts. `do`

is used as a convenient way to **sequence** and **compose** monadic computations:

- Sequence: takes any traversable data structure (e.g., a list or a tree) of monadic values and transforms it into a monadic value of the same data structure. For instance,
`[Just 1, Just 2, Just 3]`

into`Just([1, 2, 3])`

:

- Compose: the act of combining two or more monadic functions together to create a new monadic function.

Remember about `return`

? We said previously that `return`

was used to wrap a value inside a context. More specifically, `return`

wraps the value **inside a monad**; it does not end the function execution.

For example, what if we want to use the `Maybe String`

value after `return (greet name)`

? In Haskell, we can use `<-`

to bind the result of a monadic action to a variable:

Notice the multiple uses of `return`

. As we said, in Haskell, `return`

doesn’t stop the function execution; instead, it wraps a value inside a monad.

# Wrap Up

In summary, a monad is a **powerful abstraction** that extends the capabilities of applicatives. It provides a way to sequence and compose actions while preserving their contexts.

In Haskell, leveraging tools such as the `do`

notation, the `<-`

operator to bind variables, or `return`

to wrap values inside of monads allows developers to craft code that is not only concise but also remarkably powerful.

The concept of monads in Haskell goes beyond the limited scope of what we have discussed. Even I/O operations are encapsulated within monads. It allows impure actions (e.g., writing a file or reading a socket) to coexist within a pure functional framework. The monadic structure enables the sequencing and combination of I/O operation alongside any other monads.

Thank you for reading. This post wasn’t just about dissecting Haskell’s technical merits; it was also a tribute to its elegance.

Nowadays, beauty in programming is a rare concept, often pushed aside by the pursuit of efficiency. Yet, I do consider Haskell a beautiful language. Thanks to constructs like monads, imperative-style code can be reconciled with the purity and declarative nature of functional programming.

I’ve fallen in ❤️ with Haskell; could it be **your turn**?

# Edit — March 2024

Someone associated with the Haskell Foundation told me that writing about monads was a “** fatal error**”. This person also sent me The “What Are Monads?” Fallacy. TL;DR of this post:

Attempting to learn how to use monads by understanding what they are is like asking “What is a musical instrument?” and then assuming once you know the answer to that, you’ll be able to play all of the musical instruments.

First of all, I am **not** expecting you to be able to use monads in your daily work solely with this post. Obviously, it would require practice; I think we can all agree with that. However, I am slightly upset that I was told that writing about a technical concept is an error… For instance, would it be wrong if a beginner in Go wanted to write about goroutines? Under the pretext that mastering concurrency requires practice, should I tell this person this is a mistake? I don’t think so, and it would go **against** what I believe should be a programming language community: inclusive, supportive, and encouraging knowledge-sharing, regardless of skill level.

Worst case scenario: if you think this post is unnecessary (and you have the right to believe it), then just don’t read it 🙂. Ultimately, it won’t change my opinion of the language itself, and if this post makes one person want to look at Haskell, that’s already a win.