A monad is just a monoid in the category of endofunctions, what's the problem?

The quote above is from James Iry in his very funny article "Brief, Incomplete and Mostly Wrong History of Programming Languages". It is a parody of how functional programming has a lot of mathematical jargon which turns off a number of regular programmers. Right on top of the list is the explanation of the monad.

In this article, we are going to learn about monads from a very practical perspective, skipping the math.

## The Scenario

We start with two functions `inverse`

and `inc`

which return the inverse and increment respectively

```
def inverse(x):
return 1/x
def inc(x):
return x + 1
```

If we want to do these two operations in sequence, we can compose them together.

```
fn = compose(inverse, inc)
fn(10) # 1.1
```

However, the code blows up when `x`

is zero.

How do we handle the case when `x`

is zero? Normally in object-oriented programming we would throw an exception, but in functional programming that would make the function impure. In order to make a pure function implementation, all information – including errors – should be in the returned output.

Perhaps we can modify the `inverse`

function to return the success/failure status along with the output

```
def inverse(x):
if x == 0:
return (False, None)
return (True, 1/x)
```

But now that we have changed the function signature this way, we can no longer compose normally.

## Combining the two functions

We are going to create a function called `map`

to help us compose these two functions together

```
def map(val, fn):
success, output = val
if not success:
return val
return (True, fn(output))
```

This function takes in the success/failure tuple as the first parameter and the function to compose with as the second parameter.

It will check the status of the tuple. If it is empty, then it will return the same empty tuple without calling the second function. Otherwise it will take the output value in the tuple and pass it into the second function.

Here is how we can use it

```
x = 10
out = inverse(x)
out2 = map(out, inc)
print(out2) # (True, 1.1)
```

## The Maybe monad

Let us give names to everything that we have done so far.

We changed the signature of the `inverse`

function to wrap the output in a tuple and store `True`

/ `False`

alongside it. In functional programming, this structure of storing additional context with the output value is called a `Monad`

.

This particular monad is called the `Maybe`

monad and is used to denote when the data might have a value, or it might be empty.

The `Maybe`

monad can be in one of two states

`Nothing`

which represents an empty state`Just(x)`

which represents a state containing the value`x`

The `map`

function will take a `Maybe`

value and apply a function to the value. If the monad is in the `Nothing`

state then `map`

returns `Nothing`

.If the value is `Just(x)`

then it applies the function to `x`

and wraps the answer with `Just`

.

Let us rewrite our code using above terminology. We could continue using tuples as before, but it's cleaner to create classes like this:

```
class Nothing:
def map(self, fn):
return self
def __str__(self):
return "Nothing()"
class Just:
def __init__(self, val):
self.val = val
def map(self, fn):
return Just(fn(self.val))
def __str__(self):
return f"Just({self.val})"
```

We change `inverse`

to use these classes instead of wrapping with tuples

```
def inverse(x):
if x == 0:
return Nothing()
return Just(1/x)
```

Then we can compose them together as before.

```
x = 10
out = inverse(x).map(inc)
print(out) # Just(1.1)
```

## The flatmap method

In the example above, we composed with the `inc`

function via the `map`

method.

`inc`

is a normal function in the sense that it takes an ordinary value as input and returns a value as output – there is no monad involved in this function.

Compare this to `inverse`

which returns a monad.

Let us now see what happens if we compose `inverse`

with itself – we want to take a number `x`

then calculate the inverse of `x`

, and then again take the inverse of the result.

The first attempt might be something like this

`out = inverse(x).map(inverse)`

Lets follow the code execution here for `x = 10`

.

For `x = 10`

, the `inverse`

function will return `Just(0.1)`

.

Then we do `.map(inverse)`

, so line 12 will execute and it will call `fn(self.val)`

and wrap the result with `Just`

.

`fn(self.val)`

in this case is `inverse(0.1)`

which will return `Just(10)`

. And that result will be wrapped with `Just`

, so the final answer will be `Just(Just(10))`

You see the problem here. When the function to compose itself returns a `Just(x)`

, the `map`

method will wrap it with a `Just`

once more, leading to a double wrapping.

To avoid this, we introduce another method `flatmap`

. The goal of flatmap is to "flatten" the double wrapping and make it a single wrap again.

Here is how `flatmap`

should work

- In the case that the monad is in the
`Nothing`

state, then`flatmap`

should return`Nothing()`

without continuing the computation. - If it is
`Just(x)`

then`flatmap`

takes`x`

and passes it to the next function.`flatmap`

returns the output of the function as it is without wrapping it, since the function itself returns a monad.

This is what we get when we implement the steps above

```
class Nothing:
def map(self, fn):
return Nothing()
def flatmap(self, fn):
return Nothing()
def __str__(self):
return "Nothing()"
class Just:
def __init__(self, val):
self.val = val
def map(self, fn):
return Just(fn(self.val))
def flatmap(self, fn):
return fn(self.val)
def __str__(self):
return f"Just({self.val})"
```

Now the following composition will work

```
x = 10
out = inverse(x).flatmap(inverse)
print(out) # Just(10.0)
```

## Combining map and flatmap

We can now combine `map`

and `flatmap`

to do any kind of composition.

Suppose we want to compose `inverse`

→ `inc`

→ `inverse`

→ `inc`

the code is simply this

```
x = 1
out = inverse(x).map(inc).flatmap(inverse).map(inc)
print(out) # Just(1.5)
```

```
x = -1
out = inverse(x).map(inc).flatmap(inverse).map(inc)
print(out) # Nothing()
```

Whenever we compose a normal function like `inc`

we will use `map`

and when we compose with a function that itself returns a `Maybe`

then we use `flatmap`

to do the composition.

Notice something else?

Even though `inverse`

might fail, we don't need to have any if/else checks anywhere in the composition. We just compose the functions together and if the complete composition succeeds then we will get `Just(x)`

as the output. If any part of the composition fails, we get `Nothing`

as the output.

This code using monads is very easy to read.

## Summary

In this article, we saw how to use the `Maybe`

monad to compose together functions which might fail. The resulting code is very clean and easy to read, with no need to put if/else checks all over the place.

In the next article, we will see how we can apply the `Maybe`

monad to the robot kata.

**Did you like this article?**

If you liked this article, consider subscribing to this site. Subscribing is free.

Why subscribe? Here are three reasons:

- You will get every new article as an email in your inbox, so you never miss an article
- You will be able to comment on all the posts, ask questions, etc
- Once in a while, I will be posting conference talk slides, longer form articles (such as this one), and other content as subscriber-only