Functional Composition and Threading Calls in Java

With the simplified functional programming capabilities that have been available in Java for a while now, many of my friends are becoming more productive—even in Java. Methods are increasingly being written as free functions, often without side effects, which boosts productivity. It’s easier to compose functionality, even though defining new types is still simpler with object-oriented approaches.

Expressions are becoming clearer and easier to reason about. For example:

plus(4, inc(len("Text")));

Most readers would recognize that this evaluates to 9. The equivalent in Clojure would be:

(+ 4 (inc (count "Text")))

However, as expressions grow, they become harder to read because they’re written in reverse order relative to their evaluation. Functional languages like Clojure solve this with threading macros. For example:

(->> (count "Text")
     (inc)
     (+ 4))

In Java, we could simulate this with:

thread("Text",
       Ex::len,
       Ex::inc,
       partial(Ex::plus, 4));

Even though thread is not part of the standard library (and certainly not a macro), the result is an expression written in evaluation order. partial here performs partial application, returning a new function where the first argument has been pre-applied. For example:

public static <T, U, V> Function<U, V> partial(
   BiFunction<T, U, V> f,
   T t) {
  return u -> f.apply(t, u);
}

Below are the function definitions with obvious implementations. The toNil method will be used later:

public class Ex {
    public static int len(String s) {
        return s.length();
    }

    public static int inc(int x) {
        return x + 1;
    }

    public static int plus(int x, int y) {
        return x + y;
    }

    public static Integer toNil(Object o) {
        return null;
    }
}

Using Function Composition

Another way to write the expression in evaluation order is through function composition. Using a compose function like:

public static <A, B, V> Function<A, V> compose(
   Function<? super A, ? extends B> f1,
   Function<? super B, ? extends V> f2) {
  return a -> f2.apply(f1.apply(a));
}

We can write:

Function<String, Integer> f =
  compose(Ex::len,
    compose(Ex::inc,
      partial(Ex::plus, 4)));
f.apply("Text");

This achieves what we want—although it’s a bit nested.

With Java 8, the Function interface includes the andThen method for function composition:

Function.<String>identity()
  .andThen(Ex::len)
  .andThen(Ex::inc)
  .andThen(partial(Ex::plus, 4))
  .apply("Text");

That removes nesting, but the repeated andThen calls are verbose. Let’s fix that.

A Threading Function for Java

The thread function can be implemented imperatively:

public static <T0, T1, T2, T3>
T3 thread(T0 t0,
          Function<T0, T1> f0,
          Function<T1, T2> f1,
          Function<T2, T3> f2) {
    T1 t1 = f0.apply(t0);
    T2 t2 = f1.apply(t1);
    return f2.apply(t2);
}

Each overload handles a fixed number of functions. Since every function has its own type parameters, varargs isn’t a good fit. Ten overloads should cover most cases.

Similarly, for two functions:

public static <T0, T1, T2>
T2 thread(T0 t0,
          Function<T0, T1> f0,
          Function<T1, T2> f1) {
    T1 t1 = f0.apply(t0);
    return f1.apply(t1);
}

This avoids altering the core Function type, preserving separation of concerns and avoiding the expression problem.

Threading With Null-Safety: threadMaybe

Clojure has some->> for null-safe threading. We can create something similar:

threadMaybe("Text",
            Ex::len,
            Ex::toNil,
            Ex::inc);

Ex::inc will not be called because Ex::toNil returns null.

Here’s the implementation:

public static <T0, T1, T2, T3>
T3 threadMaybe(T0 t0,
               Function<T0, T1> f0,
               Function<T1, T2> f1,
               Function<T2, T3> f2) {
    if (t0 == null) return null;
    T1 t1 = f0.apply(t0);
    if (t1 == null) return null;
    T2 t2 = f1.apply(t1);
    if (t2 == null) return null;
    return f2.apply(t2);
}

Conditional Logic With Fewer Boilerplate

This pattern also applies to the cond function from the previous post. With different arities, we avoid manually creating an iterable and the catch-all guard:

public static <T> T cond(
   Supplier<Boolean> t0, Supplier<? extends T> v0,
   Supplier<Boolean> t1, Supplier<? extends T> v1,
   Supplier<Boolean> t2, Supplier<? extends T> v2,
   Supplier<? extends T> v3) {
   if (t0 != null && Boolean.TRUE.equals(t0.get()))
     return v0 == null ? null : v0.get();
   else if (t1 != null && Boolean.TRUE.equals(t1.get()))
     return v1 == null ? null : v1.get();
   else if (t2 != null && Boolean.TRUE.equals(t2.get()))
     return v2 == null ? null : v2.get();
   return v3 == null ? null : v3.get();
}

Functional composition doesn’t have to be reversed—even in Java. Threading-style expressions can be clearer and more declarative. It might take a few helper functions, but the result is more readable and easier to reason about.

Side note: Let’s continue making Java just a little more functional.


All #art #clojure #csharp #data-structures #database #datomic #emacs #functional #haskell #history #immutability #java #jit #jmm #lambdas #lisp #pioneers #poetry #programming #programming-philosophy #randomness #rant #reducers #repl #smalltalk #sql #threads #women