Learn Java

Java Currying

Since functions can be considered as objects, they can be returned as results from other functions. It allows us to use a special style of programming where we convey arguments to a function one by one and obtain functions as intermediate results. This is a fairly advanced technique, so you won't have to use it every time you can, but it is useful to be aware of such an option.

Returning functions

When we consider a function as an object we can accept it as an argument and return it as a value.

Here's an example:

public static IntBinaryOperator sumF(IntUnaryOperator f) {
    return (a, b) -> f.applyAsInt(a) + f.applyAsInt(b);
} 

The sumF method accepts an operator f with an integer argument and returns another operator with two integer arguments. In the method body, an anonymous function that takes two arguments is constructed and returned. This function applies f to each of its arguments and summarizes the results.

What can we do now? Let's just return the function from the method, save it to the sumOfSquares variable and apply some values to it:

// build a new sumOfSquares operator
IntBinaryOperator sumOfSquares = sumF(x -> x * x);

// the sum is equal to 125 (5 * 5 + 10 * 10)
long sum = sumOfSquares.applyAsInt(5, 10);

Here is a code block with some more examples:

// sum of two identities: 0 + 10 = 10
long sumOfIdentities = sumF(x -> x).applyAsInt(0, 10);

// sum with coefficients: 10 * 2 + 11 * 2 = 42
long sumWithCoefficient = sumF(x -> x * 2).applyAsInt(10, 11);

// sum of two cubes: 3 * 3 * 3 + 8 * 8 * 8 = 539
long sumOfCubes = sumF(x -> x * x * x).applyAsInt(3, 8);

As you can see, the possibility of returning functions provides an easy way to build complex and generalized functions.

Currying functions

Currying is a technique for translating the evaluation of a function that takes multiple parameters into evaluating a sequence of functions, each with a single argument. The technique is named after mathematician Haskell Curry who originally developed it. Let's see how to use it in Java.

First, let's compare a regular function and a curried function in this code snippet:

IntBinaryOperator notCurriedFun = (x, y) -> x + y; // not a curried function

IntFunction<IntUnaryOperator> curriedFun = x -> y -> x + y; // a curried function

We can define a curried function with three arguments and then apply arguments one by one.

// curried function
IntFunction<IntFunction<IntFunction<Integer>>> fff = x -> y -> z -> x * y + z;

// fff returns a curried function y -> z -> 2 * y + z
IntFunction<IntFunction<Integer>> ff = fff.apply(2);

// ff returns a curried function z -> 2 * 3 + z
IntFunction<Integer> f = ff.apply(3);

// f returns 7
int result = f.apply(1);

A shorter example:

// here the result is equal to 153
int anotherResult = fff.apply(10).apply(15).apply(3);

Let's rewrite the sumF method from the earlier example. Instead of returning a function from it, we can write a curried function and then use it in the same way:

Function<IntUnaryOperator, IntBinaryOperator> sumF = 
        (f) -> (a, b) -> f.applyAsInt(a) + f.applyAsInt(b);

// build a new sumOfSquares operator in terms of sumF
IntBinaryOperator sumOfSquares = sumF.apply(x -> x * x);

// the sum is equal to 125 again
long sum = sumOfSquares.applyAsInt(5, 10);

As you see, returning functions and currying are very close concepts and both are based on closures.

An example of currying

Suppose we would like to say "Hi" to our friends and "Hello" to our business partners. We can create a function that has two arguments: what and who. The function will apply what depending on the context.

Function<String, Consumer<String>> say = what -> who -> System.out.println(what + ", " + who);

The friends' context:

List<String> friends = Arrays.asList("John", "Neal", "Natasha");
Consumer<String> sayHi = say.apply("Hi");

// many lines of code...

friends.forEach(sayHi);

The partner's context:

List<String> partners = Arrays.asList("Randolph Singleton", "Jessie James");
Consumer<String> sayHello = say.apply("Hello");

// many lines of code...

partners.forEach(sayHello);

The result:

Hi, John
Hi, Neal
Hi, Natasha
Hello, Randolph Singleton
Hello, Jessie James

In the real situation, we can get the list of persons from a database and pass the consumer as an argument from another part of our program.

Conclusion

Returning functions and currying are powerful techniques in functional programming that enable more flexible, modularand expressive code by allowing functions to be passed as arguments and returned as values. While not always necessary, these approaches are valuable tools to consider for building complex and reusable function logic in Java.

Written by

Master Java by choosing your ideal learning course

View all courses

Create a free account to access the full topic

Sign up with Google
Sign up with Google
Sign up with JetBrains
Sign up with JetBrains
Sign up with Github
Sign up with GitHub
Coding thrill starts at Hyperskill
I've been using Hyperskill for five days now, and I absolutely love it compared to other platforms. The hands-on approach, where you learn by doing and solving problems, really accelerates the learning process.
Aryan Patil
Reviewed us on