Let’s see some examples of how to achieve currying in Java.

You can find the complete code for this here: curry_example.jsh.

Currying is taking a function with n parameters and returning a function with one parameter that returns a function with one parameter until you reach the n parameters of the original function and then you get the result.

The following method gets the curried version of a method with the signature:

int methodName(int a, int b)

And returns the curried version:

public static Function<Integer, Function<Integer, Integer>> toCurry(
    BiFunction<Integer, Integer, Integer> function) {
  return v -> v1 -> function.apply(v, v1);
}

Then we can use it to get curried versions of such methods:

var curriedAdd = toCurry(Math::addExact);
var curriedMultiply = toCurry(Math::multiplyExact);

And use them like this:

System.out.format("curriedAdd(2)(10): %d\n",curriedAdd.apply(2).apply(10));
System.out.format("curriedMultiply(2)(10): %d\n",curriedMultiply.apply(2).apply(10));

In the following example we use the curried versions to first add 1 and then multiply by 2 all the elements of a stream

var add1 = toCurry(Math::addExact).apply(1);
var multiplyBy2 = toCurry(Math::multiplyExact).apply(2);
List.of(0, 1, 2, 3, 4).stream().map(add1).map(multiplyBy2).forEach(System.out::println);

We can use currying as a form of partial application, imagine we have the following method:

public static String wget(
    int connectionTimeout,
    int readTimeout,
    boolean followRedirects,
    String requestMethod,
    String address) {
  try {
    URL url = new URL(address);
    HttpURLConnection con = (HttpURLConnection) url.openConnection();
    con.setRequestMethod(requestMethod);
    con.setConnectTimeout(connectionTimeout);
    con.setReadTimeout(readTimeout);
    con.setInstanceFollowRedirects(followRedirects);
    return address + " " + con.getResponseCode();
  } catch (Exception e) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    e.printStackTrace(pw);
    String stackTrace = sw.toString();
    return address + " " + stackTrace.substring(0, stackTrace.indexOf("\n"));
  }
}

This method returns the http status or part of the exception if any was thrown.

If we use it like that we would have to be setting all the parameters everytime we want to use the function, but if we have a curried version:

public static Function<Integer, Function<Boolean, Function<String, Function<String, String>>>>
    cwget(int v) {
  return v1 -> v2 -> v3 -> v4 -> wget(v, v1, v2, v3, v4);
}

We could then set the parameters we have:

var get = cwget(100).apply(100).apply(false).apply("GET");

And then just fill the renaming parameter when using the function:

List.of(
        "https://www.google.com",
        "https://www.wikipedia.org",
        "asdf",
        "https://docs.oracle.com/javase/10/docs/api/java/net/package-summary.html",
        "https://jsedano.dev",
        "https://raw.githubusercontent.com/center-key/clabe-validator/main/clabe.ts")
    .parallelStream()
    .map(get)
    .forEach(System.out::println);

Download the complete code from this post here: curry_example.jsh.