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

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

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

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • What Is Applicative? Basic Theory for Java Developers
  • What Is a Functor? Basic Theory for Java Developers
  • What Is a Monad? Basic Theory for a Java Developer
  • How to Convert XLS to XLSX in Java

Trending

  • AI’s Role in Everyday Development
  • Concourse CI/CD Pipeline: Webhook Triggers
  • Emerging Data Architectures: The Future of Data Management
  • Medallion Architecture: Efficient Batch and Stream Processing Data Pipelines With Azure Databricks and Delta Lake
  1. DZone
  2. Coding
  3. Java
  4. Lifting Functions to Work With Java Monads

Lifting Functions to Work With Java Monads

Want to take a more functional approach to Java? The language doesn't offer a lot of options, but here is some custom code you can use for method lifting.

By 
Paweł Szeliga user avatar
Paweł Szeliga
·
Jul. 24, 17 · Tutorial
Likes (27)
Comment
Save
Tweet
Share
14.9K Views

Join the DZone community and get the full member experience.

Join For Free

Stream and Optional classes, added to Java 8, allow you to have some fun with functional programming. The problem is that Java is still missing quite a lot to be taken seriously as a functional programming language. Lambda notation and two monads (Optional and Stream) are just the tip of the iceberg. This leads to libraries like vavr or functionaljava — both deriving from the purely functional language Haskell.

One of the first things you need to get rid of when trying to be more functional is the attempt to unwrap monads too early. It usually involves using methods like Optional.get() or Stream.collect() where there is no need yet. Sometimes, though, Java doesn't help with that, so let me give you some custom code. This article will bring you closer to the concept of method lifting.

Calculations on Optionals

Suppose we have a nice API we would like to use to calculate numbers:

public interface Math {
    int multiply(int a, int b);
    double divide(int a, int b);
    ..
}


We would like to use it to do some calculations on numbers wrapped with Optional:

public interface NumberProvider {
    Optional<Integer> getNumber();
}


Let's say we want to write a method, which returns the result of the division of two numbers wrapped with Optional, or empty Optional if any one of them is empty (we skip the zero divisor case here). It may look something like this:

public Optional<Double> divideFirstTwo(NumberProvider numberProvider, Math math) {
    Optional<Integer> first = numberProvider.getNumber();
    Optional<Integer> second = numberProvider.getNumber();
    if(first.isPresent() && second.isPresent()) {
        double result = math.divide(first.get(), second.get());
        return Optional.of(result);
    } else {
        return Optional.empty();
    }
}


That's rather nasty. It involves a lot of code, the only purpose of which is to wrap/unwrap the Optional. Let's try to make it more functional:

public Optional<Double> divideFirstTwo(NumberProvider numberProvider, Math math) {
    return numberProvider.getNumber()
           .flatMap(first -> numberProvider.getNumber()
                                     .map(second -> math.divide(first, second)));
}


That's much better. It turns out that invoking flatMap on the first monad and map on the second one inside the lambda can be extracted even further to the method-called lift:

public interface Optionals {

    static <R, T, Z> BiFunction<Optional<T>, Optional<R>, Optional<Z>> lift(BiFunction<? super T, ? super R, ? extends Z> function) {
        return (left, right) -> left.flatMap(leftVal -> right.map(rightVal -> function.apply(leftVal, rightVal)));
    }
}


The lift is able to promote any function, which takes two arguments, to the function with the arguments and the result type wrapped with Optional. It actually adds Optional behavior to the function in such a way that if one of the arguments is empty, then the result will also be empty. If the JDK extracted flatMap and map methods to some common interface, for example, Monad, then we could write one lift function for every instance of a Java monad (Stream, Optional, custom classes). Unfortunately, we need to do this copy-pasting for every instance. The final code for divideFirstTwo becomes:

import static com.ps.functional.monad.optional.Optionals.lift;
...

    public Optional<Double> divideFirstTwo(NumberProvider numberProvider, Math math) {
        return lift(math::divide).apply(numberProvider.getNumber(), numberProvider.getNumber());
    }


Summary

I hope this article encouraged you to play with functional style in Java. The JDK needs to be greatly improved for the language to be called functional in the future. Unfortunately, Java 9 doesn't promise any major improvements besides a few additional methods. The source code can be found here.

Java (programming language) Monad (functional programming)

Published at DZone with permission of Paweł Szeliga. 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
  • What Is a Monad? Basic Theory for a Java Developer
  • How to Convert XLS to XLSX in Java

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!