In this text, the final summary of my categories theory series, I will use my spotlight and take a closer look at the relations between all three previously described functional containers, namely: Functor, Monad, and Applicative.

Below, you will find a comparison of them in terms of:

- Theory
- Laws
- Methods
- Possibilities & Use Cases

**Theory**

**Functor**

In category theory, a **Functor** represents the mapping between two categories.

In software, Functors can be viewed as a util class that allows us to perform a mapping operation over values wrapped in some context.

It can also be viewed as an interface proving a certain ability, namely the ability to transform the state of an object. In more functional focused languages like Scala or Haskell, Functor is a typeclass.

There are a few types of Functors. However, their in-depth description and analysis is quite out of the scope of this article, but do not be afraid – I added links to other resources that will help you deepen your knowledge.

Functor types:

There are no good built-in counterparts of Functors in Java and other modern-day programming JVM languages. Nevertheless, they are reasonably easy to implement manually. Probably the best counterpart can be found in the Scala library called Cats or in Haskell.

**Applicative**

In category theory, an** Applicative**, also known as an Applicative Functor, is a concept that generalizes a Functor by allowing for operations not just on the values inside a context but also on functions within a context.

It sits between Functors and Monads in terms of expressive power. While less powerful than Monads, Applicatives are more structured than Functors.

By the book, Applicatives do not allow chain operations in the same ways as Monads do – with the output of one operation being the input of the other. On the other hand, unlike Functors, Applicatives allow us to sequence our computations.

Unfortunately, such a relation is quite hard to achieve in the world of software as in the end, the underlay data will end up being used as input for the next.

**Monad**

In the world of software, we can view it as a wrapper that puts our value in some context and allows us to perform operations, specifically operations that return results wrapped in the Monadic context, on the value.

Monads are mostly used to handle all kinds of side effects:

- Performing I/O operation
- Handling operations that can throw exceptions
- Handling async operations

Another important point is that we can chain the operations in such a manner that the output of an operation at any step is the input to the operation at the next step. Such behavior allows us to write very declarative code with minimal boilerplate.

Contrary to Functor, Monad has quite a lot of implementations built in modern-day programming languages:

- Scala (Option, Try, Either, … and more)
- Haskell (IO, …. and more ),
- Java (Optional, not exactly a Monad, but close)

From both practical and theoretical points of view, it is also the most powerful of all the three concepts. The complete hierarchy of all three in terms of pure mathematical power looks more or less like this, going from least to most powerful:

**Laws**

**Functor**

If we want to implement a Functor, our implementation needs to obey two laws: **Identity** and **Associativity**

**1. Identity**

Mapping values inside the Functor with the identity function should always return an unchanged value.

**2. Associativity**

In the chain of function applications, it should not matter how functions are nested.

**Applicative**

If we want to implement Applicative, our implementation needs to obey four laws namely: **Identity**, **Homomorphism**, **Interchange**, **Composition**.

**1. Identity**

Applying the identity function to a value inside the context should always return an unchanged value.

**2. Homomorphism**

Applying a function in context to a value in context should give the same result as applying the function to the value and then putting the result in context with the usage of pure.

**3. Interchange**

Applying the function with context f to a value with context should be the same as applying the wrapped function, which supplies the value as an argument to another function, to the function with context f.

**4. Composition**

Applying the function with context f and then the function with context g should give the same results as applying functions composition of f and g together within the context.

**Alternative Applicative**

There is also an equivalent version of Applicative whose implementation needs to obey three laws: **Associativity**, **Left Identity,** and **Right Identity**.

**1. Left identity**

If we create a new Applicative and bind it to the function, the result should be the same as applying the function to the value.

**2. Right identity**

The result of binding a unit function to the Applicative should be the same as the creation of a new Applicative.

**3. Associativity**

In the chain of function applications, it should not matter how functions are nested.

If you would like to take a closer look at the laws of this version of **Applicative** you can notice that they are the same as the Functor laws. The change in the **Identity Law** originates from the difference in the setup of both concepts. In particular from the existence of the **pure **method in Applicative, in a way, we have to check one more case.

**Monad**

If we want to implement a Monad, we have to obey three laws: **Left** **Identity**, **Right Identity**, and **Associativity**.

**1. Left identity**

If we create a new Monad and bind it to the function, the result should be the same as applying the function to the value.

**2. Right identity**

The result of binding a unit function to a Monad should be the same as the creation of a new Monad.

**3. Associativity**

In the chain of function applications, it should not matter how functions are nested.

Same as in the case of **Applicative**, **Monads** Laws are very similar to **Functor** laws. What is more, **Monad** Laws are exactly the same as **Applicative** Laws, the only difference is the concept described in the Laws, the condition remains unchanged.

The relation between the three in terms of which concepts extends which will look more or less like in the picture below, basically, everything is a Functor.

**Methods**

To implement any of the structures, you will need a language with generics support as a parameterized type M<T> is a base for any of them. On the other hand, you can just generate the code for all required types via a macro or some other construct but with generics it is significantly easier.

**Functor**

To implement **Functor,** you will have to implement only one method:

**map**, you pass a function that operates on value in context. This method should have the following signature M<U> (T ->U).

**Applicative**

To implement **Applicative **you will have to implement two methods:

**pure,**which is used to wrap your value in the**Applicative**context and has the following signature M<T>(T).**apply**(**ap**), you pass a function with context, the function then operates on the value in the context. This method should have the following signature M<U> (M<T -> U>).

**Alternative Applicative**

There is also an equivalent version of **Applicative** where we are using the **product **instead of **apply**.

**product**, you pass two different values wrapped in Applicative context and get context with both values in return. This method should have the following signature M<(T, U)>(M<T>, M<U>).

In both approaches to **Applicatives,** you get a **map** method with signature M<U> (T -> U) by their definition as all **Applicatives** are **Functors**, you.

**Monad**

To implement **Monad** you will have to implement two methods:

**unit**(**of,**) which is used to wrap the value in**Monad**context and has the following signature M<T>(T).**bind**(**flatMap**), you pass a function that operates on value in context but the result is already wrapped in**Monad**context. This method should have the following signature M<U> (T -> M<U>).

What is more, Monad is also an **Applicative** and, by extension, a **Functor**. It means that we are getting a lot of additional methods for free. In particular, a **map **method from **Functor **and **apply **or **product** methods from Applicative.

**The possibilities & use cases**

**Functor**

The **Functor** is just a util method. It does not provide anything more than simple mapping of a value inside an arbitrary context.

On the other hand, it is also the simplest and the easiest of all three concepts and can be used almost anywhere where we need to perform operations over the values in the context.

Thought **Functors** still have their limitations. By its definition, **Functor** is unable to chain computations. What is more, **Functor** does not provide a way to flatten the result of performed operations, so we can easily end up with nested types like <List<List<Long>>.

However, the possibility of performing effectfull operations without moving them out of the context is quite a good thing.

**Applicative**

Going further, we have an **Applicative** with which we can **apply** functions in context to a value with context. Additionally, with its **product** version, we can create a tuple out of two objects within the same context.

Such behavior can be extremely useful in some cases – we can use it to compose multiple effects to one effect, reducing types like List<Future> to more reasonable Future<List>.

That is the reason why **Applicatives** are a great choice for implementing concepts like **parsers**, **traversables,** or** composers** and any other use case where we need to work with many independent effects of the same type.

What is more, because the **Applicative** is a** Functor** we can also apply some operations on such output tuples via the **map** method.

**Monad**

Last but not least, we have **Monad** – the most powerful of all three structures.

Each Monad implementation represents an effect:

- emptiness (Option)
- possible failure (Try)
- result or failure of an operation (Either)
- chain of computations (IO)

What is more, Monad gives us the way to put values in such an effectfull context.

Furthermore, thanks to the **flatMap** method, we can handle nested return types. This in turn solves the issues with M<M<T>> styled types (Optional<Optional<Long>).

**Monad** implementation will automatically flatten such types to have only one effect – an operation that the **map** method from **Functor** is unable to make by its definition.

Additionally, with **Monads,** we can perform contextual operations as we can pass outputs of one operation as inputs to another achieving a nice, declarative chain of computations.

**Summary**

Functional containers are a very useful tool that can be applied to a variety of problems. In my opinion, we should find them a place in our engineering toolbox or at least be aware of them.

Just please remember that they are not a silver bullet, and making everything a **Monad** may not be the best way to write your code.

As a side note, I would like to add that these containers add another nice touch of math to the world of software engineering, which is another good point to like them, at least in my opinion.

Hope that you find my text interesting. Thank you for your time.

Comments are closed.