DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • What Is Applicative? Basic Theory for Java Developers
  • What Is a Functor? Basic Theory for Java Developers
  • A Guide to Container Runtimes
  • Inheritance in PHP: A Simple Guide With Examples

Trending

  • Enforcing Architecture With ArchUnit in Java
  • Using Python Libraries in Java
  • The Smart Way to Talk to Your Database: Why Hybrid API + NL2SQL Wins
  • How To Build Resilient Microservices Using Circuit Breakers and Retries: A Developer’s Guide To Surviving
  1. DZone
  2. Coding
  3. Languages
  4. Functional Containers Summary: Functor vs Applicative vs Monad

Functional Containers Summary: Functor vs Applicative vs Monad

In this comprehensive guide through basic functional containers Functor, Applicative, and Monad, explore possible applications and learn the math behind them.

By 
Bartłomiej Żyliński user avatar
Bartłomiej Żyliński
DZone Core CORE ·
Jan. 08, 24 · Analysis
Likes (1)
Comment
Save
Tweet
Share
4.7K Views

Join the DZone community and get the full member experience.

Join For Free

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 and 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 are 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:

  • Contravariant Functor
  • Invariant Functor
  • Opposite Functor
  • Bifunctor and Multifunctor

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.

Going 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, etc.)
  • Haskell (IO, etc.)
  • 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:Hierarchy pyramid of Functor, Applicative, and Monad

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.

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

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

Relationships between Monad, Applicative, and 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<R> (T -> R).
    map Functor

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, and the function then operates on the value in the context. This method should have the following signature: M<U> (M<T -> U>).
    Apply Applicative

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>).

Product Applicative

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

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>).
    flatMap Monad

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 and 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>>.

map Functor

However, the possibility of performing effective operations without moving them out of 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 a way to put values in such an effective 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>).flatMap Monad

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.

Monad implementationAdditionally, 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.

If you would like to deepen your knowledge of a particular container, here is the list with my articles describing all three of them in more detail.

  • What Is a Functor? Basic Theory for Java Developers
  • What Is Applicative? Basic Theory for Java Developers
  • What Is a Monad? Basic Theory for a Java Developer

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.

Review by Mateusz Borek

Container Functor Monad (functional programming) Language code Haskell (programming language)

Published at DZone with permission of Bartłomiej Żyliński. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • What Is Applicative? Basic Theory for Java Developers
  • What Is a Functor? Basic Theory for Java Developers
  • A Guide to Container Runtimes
  • Inheritance in PHP: A Simple Guide With Examples

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!