For no reason other than it’s interesting, here’s a take on Currying in Java 8. The idea of currying is to convert a function that takes n parameters into one which can receive them one by one, or indeed all at once.

By using partial application to provide some of the inputs to a function we provided a pre-loaded function that just needs the input that the recipient most cares about, in order to do its job. For example, if streaming and mapping, you really only care about the object next in the stream in order to transform it, not any other parameters kicking around that will be the same each time in the transformation function.

Here lies my attempt at a currying library:

package uk.org.webcompere; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; /** * Java implementation of currying */ public interface Curry { /** * Tagging interface for the {@link #curry(Curried)} function */ interface Curried { } /** * A nullary function - takes no parameters, returns a value. Also a supplier * with a default for {@link Supplier#get} but requires the apply function * to make it consistent with partial application. * @param <R> type of return */ @FunctionalInterface interface NullaryFunction<R> extends Curried, Supplier<R> { R apply(); default R get() { return apply(); } } /** * Unary function - takes one parameter * @param <R> return type * @param <T> parameter type */ @FunctionalInterface interface UnaryFunction<R, T> extends Curried, Function<T, R> { /** * Can be converted to nullary function by full application of a parameter. * @param t input * @return a function that evaluates using the given parameter against the UnaryFunction */ default NullaryFunction<R> asNullary(T t) { return () -> apply(t); } } /** * Binary function - takes two parameters. * @param <R> return type * @param <U> first parameter type * @param <T> second parameter type */ @FunctionalInterface interface BinaryFunction<R, U, T> extends Curried, BiFunction<U, T, R> { /** * Partial application of binary function to yield unary function where the * first parameter of the binary function has been supplied already. * @param u first input parametr for partial application * @return unary function which takes next input parameter for full application */ default UnaryFunction<R, T> apply(U u) { return t -> apply(u, t); } /** * Supply all values to return a supplier/nullary function * @param u first parameter * @param t second parameter * @return a nullary function that returns the equivalent of calling the binary function * with all its inputs */ default NullaryFunction<R> asNullary(U u, T t) { return () -> apply(u, t); } } /** * A ternary function, which takes three inputs and returns a value. * @param <R> return type * @param <V> first input parameter type * @param <U> second input parameter type * @param <T> third input parameter type */ @FunctionalInterface interface TernaryFunction<R, V, U, T> extends Curried { /** * The function that's being wrapped for partial application * @param v input 1 * @param u input 2 * @param t input 3 * @return the result of applying the function */ R apply(V v, U u, T t); /** * Partially apply the first two parameters to get a Unary function for the third * @param v first parameter * @param u second parameter * @return a function that can be called with one parameter */ default UnaryFunction<R, T> apply(V v, U u) { return t -> apply(v, u, t); } /** * Partially apply the first parameter to get a Binary function for the third * @param v first parameter * @return a function that can be called with the remaining parameters */ default BinaryFunction<R, U, T> apply(V v) { return (u, t) -> apply(v, u, t); } /** * Supply all values to return a supplier/nullary function * @param v first parameter * @param u second parameter * @param t third parameter * @return a nullary function that returns the equivalent of calling the function * with all its inputs */ default NullaryFunction<R> asNullary(V v, U u, T t) { return () -> apply(v, u, t); } } /** * A bit of syntactic sugar to convert a plain old function into one of the above types * @param t a functional interface implementation - probably a method reference * @param <T> type of function we're going to return * @return a function cast as one of the partially applicable types */ static <T extends Curried> T curry(T t) { return t; } }

And here are some unit tests that give you a taste of using it.

package uk.org.webcompere; import static uk.org.webcompere.Curry.curry; import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import java.util.stream.Stream; import org.junit.Test; public class CurryTest { public static String identity(String a) { return a; } public static String concat(String a, String b) { return a + b; } public static String concat(String a, String b, String c) { return a + b + c; } @Test public void curryTernary() { Curry.TernaryFunction<String, String, String, String> function = curry(CurryTest::concat); assertThat(function.apply("a").apply("b").apply("c")).isEqualTo("abc"); assertThat(function.apply("a", "b").apply("c")).isEqualTo("abc"); assertThat(function.apply("a", "b", "c")).isEqualTo("abc"); assertThat(function.asNullary("a","b","c").apply()).isEqualTo("abc"); } @Test public void rightHon() { Curry.TernaryFunction<String, String, String, String> function = curry(CurryTest::concat); Stream<String> names = Stream.of("Bill", "Bob", "Ben"); assertThat(names.map(function.apply("Right", " hon ")) .collect(toList())).containsExactly("Right hon Bill", "Right hon Bob", "Right hon Ben"); } }