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

Enterprise AI Trend Report: Gain insights on ethical AI, MLOps, generative AI, large language models, and much more.

2024 Cloud survey: Share your insights on microservices, containers, K8s, CI/CD, and DevOps (+ enter a $750 raffle!) for our Trend Reports.

PostgreSQL: Learn about the open-source RDBMS' advanced capabilities, core components, common commands and functions, and general DBA tasks.

AI Automation Essentials. Check out the latest Refcard on all things AI automation, including model training, data security, and more.

Related

  • Java 22 Brings Major Enhancements for Developers
  • The Power of LLMs in Java: Leveraging Quarkus and LangChain4j
  • How To Get Started With New Pattern Matching in Java 21
  • Getting Started With NCache Java Edition (Using Docker)

Trending

  • Harmonizing AI: Crafting Personalized Song Suggestions
  • Deploying to Heroku With GitLab CI/CD
  • C4 PlantUML: Effortless Software Documentation
  • AWS Fargate: Deploy and Run Web API (.NET Core)
  1. DZone
  2. Coding
  3. Java
  4. Higher-order Functions, Functions Composition, and Currying in Java 8

Higher-order Functions, Functions Composition, and Currying in Java 8

By 
Mario Fusco user avatar
Mario Fusco
·
Jun. 17, 14 · Interview
Like (11)
Save
Tweet
Share
54.0K Views

Join the DZone community and get the full member experience.

Join For Free

The main concept behind functional programming is that data and behaviors can be treated and manipulated uniformly and in the same way. In practical terms this means that it is possible to pass to a method both values and functions and in the same way the method itself can return either a value or a function. With this regard a function accepting one or more functions as argument and/or returning another function as result is called an higher-order function. The purpose of this article is explaining what functions composition and currying are demonstrating why they can be both seen as special cases of higher-order functions and showing how to use them with a practical example in Java 8.

Let's define a Converter as an object with a single method that takes as input a conversion rate and the value to be converted and returns the conversion's result that is obtained by simply multiplying these 2 arguments. Given this description the Converter can be seen as a function of 2 parameters and then defined as a particular implementation of the BiFunction interface taking 2 Doubles, the conversion rate and the value to be converted, as arguments and returning a third Double that is the converted value.

public class Converter implements 
                       BiFunction<Double, Double, Double> {
    @Override
    public Double apply(Double conversionRate, Double value) {
        return conversionRate * value;
    }
}

In this way we could for instance convert 10, 20 or 50 miles in kilometers by instancing a new Converter and passing to it the conversion rate between miles and kilometers together with the number of miles to be converted.

Converter converter = new Converter();
double tenMilesInKm = converter.apply(1.609, 10.0);
double twentyMilesInKm = converter.apply(1.609, 20.0);
double fiftyMilesInKm = converter.apply(1.609, 50.0);

This works, but there is something not very practical with this approach: we are obliged to repeat the same conversion rate for all the invocation of our converter. It would be better if our converter could offer an easy way to obtain a specialized version of it just converting miles to kilometers. In other words we would like to create a Function out of the original BiFunction having set one of the 2 arguments of the BiFunction to a fixed value, that in our case is the miles/km conversion rate. In functional programming this particular operation is called currying.

Currying

Unfortunately the native Java 8 BiFunction interface doesn't provide currying out of the box. Nevertheless it is very easy to develop a custom extension of the original BiFunction interface having 2 additional default methods that allow to set one of the 2 arguments of the BiFunction to a fixed value.

@FunctionalInterface
public interface ExtendedBiFunction<T, U, R> 
                                   extends BiFunction<T, U, R> {

    default Function<U, R> curry1(T t) {
        return u -> apply(t, u);
    }

    default Function<T, R> curry2(U u) {
        return t -> apply(t, u);
    }
}

The methods curry1 and curry2 fix respectively the first and the second argument of the BiFunction and return a Function having as single argument the one of the two that is still remained unfixed. If we now change our Converter just making it to implement this ExtendedBiFunction interface

public class Converter 
        implements ExtendedBiFunction<Double, Double, Double>

it's possible to obtain a Function converting miles to kilometers fixing the first argument of the converter to the appropriate conversion rate.

Function<Double, Double> mi2kmConverter = 
        converter.curry1(1.609);
double tenMilesInKm = mi2kmConverter.apply(10.0);
double twentyMilesInKm = mi2kmConverter.apply(20.0);
double fiftyMilesInKm = mi2kmConverter.apply(50.0);

Of course we can repeat this specialization process how many times we want for example to obtain a converter from ounces to grams.

Function<Double, Double> ou2grConverter = 
        converter.curry1(28.345);
double tenOuncesInGr = ou2grConverter.apply(10.0);
double twentyOuncesInGr = ou2grConverter.apply(20.0);
double fiftyOuncesInGr = ou2grConverter.apply(50.0);

More formally currying is a technique where a function f of two arguments (x and y say) is seen instead as a function g of one argument which returns a function also of one argument. The value returned by the latter function is the same as the value of the original function.

f(x,y) = (g(x))(y)

Of course the same principle can be generalized to transform a function of n arguments in one of n-1 arguments having fixed the remaining one. It's also easy to see that the curry1 and curry2 methods are examples of higher-order function since they returns a Function as result.

Function composition

In the biggest part of cases a multiplication, as the one performed by our Converter class, is all you need to do to convert a unit measure into another. However there are some situations for which this couldn't be enough. For instance the formula to convert a Celsius temperature in a Farenheit one is:

F = C * 9/5 + 32

After having multiplied the Celsius temperature by the 9/5 factor, you still have to add 32 in order to obtain the corresponding Farenheit value. Is there a way to obtain a Function converting Celsius to Farenheit degrees from our generic Converter? This is a case where functions composition can come to the rescue. In fact the native Java 8 Function interface already provides a default method named andThen accepting another Function as argument and returning a third Function as result (another higher-order function) that is the composition of the first 2. The resulting composed function first applies the original function to its input, and then applies the function passed as argument to the result. In this way the Celsius to Farenheit conversion function can be obtained currying the generic converter with the 9/5 factor and then applying a further function that increments the result of the first one of 32.

Function<Double, Double> celsius2farenheitConverter = 
        converter.curry1(9.0/5).andThen(n -> n + 32);
double tenCInF = celsius2farenheitConverter.apply(10.0);
double twentyCInF = celsius2farenheitConverter.apply(20.0);
double fiftyCInF = celsius2farenheitConverter.apply(50.0);

What about the opposite conversion? We have to use the formula:

C = (F - 32) * 5/9

meaning that this time the subtraction has to be performed before the multiplication by the conversion rate. In other words we need to compose the original conversion function with another function (the subtraction) that has to be applied before it and not after as we did above. The native Java 8 Function interface already provides a compose method allowing to achieve this, but for some reason the same method is not available also on the BiFunction interface. This is not too bad since we can add it (actually we need 2 of them, one for each argument) to our ExtendedBiFunction interface.

@FunctionalInterface
public interface ExtendedBiFunction<T, U, R> 
                                   extends BiFunction<T, U, R> {

    default Function<U, R> curry1(T t) {
        return u -> apply(t, u);
    }

    default Function<T, R> curry2(U u) {
        return t -> apply(t, u);
    }

    default <V> ExtendedBiFunction<V, U, R> compose1(Function<? super V, ? extends T> before) {
        return (v, u) -> apply(before.apply(v), u);
    }

    default <V> ExtendedBiFunction<T, V, R> compose2(Function<? super V, ? extends U> before) {
        return (t, v) -> apply(t, before.apply(v));
    }
}

Here the compose1 higher-order function composes the BiFuction with the before Function and returns another BiFunction that, when applied, first transform its first argument with the before Function and then applies the original BiFunction with the transformed first argument and the unchanged second one. The compose2 method implements exactly the same principle to the second argument of the BiFunction leaving unchanged the first one. We can now obtain the Farenheit to Celsius converter composing the generic Converter with a Function that subtract 32 from the value to be converted before before to currying the first argument fixing it to 5/9.

Function<Double, Double> farenheit2celsiusConverter = 
        converter.compose2((Double n) -> n - 32).curry1(5.0/9);
double tenFInC = farenheit2celsiusConverter.apply(10.0);
double twentyFInC = farenheit2celsiusConverter.apply(20.0);
double fiftyFInC = farenheit2celsiusConverter.apply(50.0);

If you want you can both deepen your knowledge of the new API introduced into Java in its 8th major release and learn how to leverage the new functional features of the language reading the Java 8 in Action book that I just finished to write together with Raoul-Gabriel Urma and Alan Mycroft.

Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Java 22 Brings Major Enhancements for Developers
  • The Power of LLMs in Java: Leveraging Quarkus and LangChain4j
  • How To Get Started With New Pattern Matching in Java 21
  • Getting Started With NCache Java Edition (Using Docker)

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send 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: