Lesson Introduction

Monads can make your code much cleaner and safer, especially for handling errors and chaining operations. Today, we aim to understand what a monad is, specifically the Maybe monad, and see how it helps in functional programming. By the end of this lesson, you'll know how to create and use a Maybe monad and how to chain operations using the bind method.

What is a Monad?

A monad is a design pattern used in functional programming to handle program logic that involves wrapping a value, performing operations, and managing side effects. Monad is an extension of Functor. Effectively, Monad does the same thing as the Functor, but it provides additional logic to handle all possible scenarios like data of incorrect type or None instead of value.

Creating the Maybe Monad

The Maybe monad represents values that might or might not exist. It helps avoid errors when you try to use missing values. Let's create the Maybe monad in Python step-by-step:

This constructor holds a value that can either be None (absence of value) or any other type T.

Checking State in Maybe Monad

To work with our Maybe monad, we check if it contains a value or not. We do this using the is_just method.

is_just checks if the Maybe monad contains a value.

Binding Functions with Maybe Monad

Next, we implement the map method, similar to the one we had in functors.

It works simply. First, we check if our monad has a value. If it does, we apply the given function to it and wrap the result back into monad. Otherwise, we return a new empty monad. So far, monad works in the same manner as a functor, but there is a problem with this approach. Let's explore it.

Wrapping Problem

Consider a function that returns a Maybe itself:

This division function handles division by zero by returning an instance of Maybe class. If b is zero, the result will be a None, safely stored inside the monad.

Consider the following scenario: we use Maybe to divide 10 by 2 with safe_divide:

Note that safe_divide returns an instance of Maybe, and the map method of Maybe wraps it's result in Maybe. A lot of maybes here, right? It is! In this case, the result variable will have the type Maybe[Maybe[int]], which is a nested Maybe. It is a problem, as it doesn't allow us to work with result further in the same manner as with the original value.

Solving the Wrapping Problem

To solve it, let's add a new method to our monad called join. It will unwrap the monad's value, returning it to its normal state.

The implemented join method works straightforward:

  1. It checks if the monad holds a value.
  2. It checks if the value is also a Maybe instance, meaning we have a situation of nested monads.
  3. If both conditions are true, it unwraps the monad by assigning self._value to the inner value.
  4. If conditions are false, meaning there is no nested Maybe, it simply does nothing.
Combining Methods

Finally, we combine map and join into a single bind method to make it easy to use and clear:

This time, we use bind method instead of map method. It unwraps the nested Maybe, making sure result is of type Maybe[int] as expected.

Lesson Summary

Today, we explored monads and their benefits in functional programming. Specifically, we learned about the Maybe monad, how to check its state using is_just, and how to handle operations properly using the bind method.

Now that we've covered the theory and seen some examples, it's time to put your knowledge into practice. You'll work on exercises to help solidify your understanding of the Maybe monad and its applications. Happy coding!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal