Partial Function Application in Java 8
I’ve been doing some Java lately, and the new functional additions in Java 8 are interesting. Java still has a long way to go, but they made functional programming in Java easier. For example, we can now do simple partial application.
For reference, here is what partial application looks like in Clojure (using the built in function):
(defn add [x y] (+ x y))
(def adder (partial add 5))
(adder 1)
;; 6
And here is a simple implementation with Java 8:
public class Example {
public static int add(int x, int y) {
return x + y;
}
public static <T, U, R> Function<U, R> partial(BiFunction<T, U, R> f, T x) {
return (y) -> f.apply(x, y);
}
public static void main(String[] args) {
Function<Integer, Integer> adder = partial(Example::add, 5);
System.out.println(adder.apply(2)); // 7
}
}
We can also define functions using lambdas:
BiFunction<Integer, Integer, Integer> minus = (x, y) -> x - y;
Function<Integer, Integer> subtractor = partial(minus, 10);
System.out.println(subtractor.apply(4)); // 6
This Java version is more limited than the Clojure one. It can only take a two argument function, whereas Clojure’s supports arbitrarily many arguments.
Java has a BiFunction
(function with two arguments), but no
TriFunction
or more. If we want to write a version of partial
that
accepts more arguments, we need to write our own TriFunction
:
@FunctionalInterface
interface TriFunction<T, U, V, R> {
R apply(T a, U b, V c);
}
Then, we can make a version of partial that accepts a TriFunction
:
public static <T, U, V, R> Function<V, R> partial(TriFunction<T, U, V, R> f, T x, U y) {
return (z) -> f.apply(x, y, z);
}
And use it in much the same way:
Function<Integer, Integer> adder3 = partial(Example::add3, 1, 2);
System.out.println(adder3.apply(3)); // 6
This version allows us to pass in two arguments to partial
, but it
doesn’t allow us to pass in a single argument and return a function that
takes two arguments:
BiFunction<Integer, Integer, Integer> adder3_2 = partial(Example::add3, 1);
System.out.println(adder3_2.apply(2, 3)); // 6
To implement this, we need yet another version of partial
:
public static <T, U, V, R> BiFunction<U, V, R> partial(TriFunction<T, U, V, R> f, T x) {
return (y, z) -> f.apply(x, y, z);
}
The moral of the story is that Java 8 allows us to implement partial
,
but we have to implement all of the variations separately.
Here is the full example:
@FunctionalInterface
interface TriFunction<T, U, V, R> {
R apply(T a, U b, V c);
}
public class Example {
public static int add(int x, int y) {
return x + y;
}
public static int add3(int x, int y, int z) {
return x + y + z;
}
public static <T, U, R> Function<U, R> partial(BiFunction<T, U, R> f, T x) {
return (y) -> f.apply(x, y);
}
public static <T, U, V, R> Function<V, R> partial(TriFunction<T, U, V, R> f, T x, U y) {
return (z) -> f.apply(x, y, z);
}
public static <T, U, V, R> BiFunction<U, V, R> partial(TriFunction<T, U, V, R> f, T x) {
return (y, z) -> f.apply(x, y, z);
}
public static void main(String[] args) {
Function<Integer, Integer> adder = partial(Example::add, 5);
System.out.println(adder.apply(1)); // 6
BiFunction<Integer, Integer, Integer> minus = (x, y) -> x - y;
Function<Integer, Integer> subtractor = partial(minus, 10);
System.out.println(subtractor.apply(4)); // 6
Function<Integer, Integer> adder3 = partial(Example::add3, 1, 2);
System.out.println(adder3.apply(3)); // 6
BiFunction<Integer, Integer, Integer> adder3_2 = partial(Example::add3, 1);
System.out.println(adder3_2.apply(2, 3)); // 6
}
}