Transformations
Curry, uncurry, compose and reshape โ utilities for working with functions as values.
com.svenruppert.functional.Transformations is a static utility class that collects everything you wish was on Function itself.
Currying & uncurrying
Most useful when memoizing multi-arg functions or building configurable factories.
// BiFunction <-> Function<A, Function<B, R>>
<A, B, R> Function<BiFunction<A, B, R>, Function<A, Function<B, R>>> curryBiFunction();
<A, B, R> Function<Function<A, Function<B, R>>, BiFunction<A, B, R>> unCurryBiFunction();
// TriFunction <-> Function<A, Function<B, Function<C, R>>>
<A, B, C, R> Function<TriFunction<A, B, C, R>, Function<A, Function<B, Function<C, R>>>>
curryTriFunction();
<A, B, C, R> Function<Function<A, Function<B, Function<C, R>>>, TriFunction<A, B, C, R>>
unCurryTriFunction();
// Same pairs for Checked* variants
curryCheckedBiFunction() unCurryCheckedBiFunction()
curryCheckedTriFunction() unCurryCheckedTriFunction()
BiFunction<Integer, Integer, Integer> add = Integer::sum;
Function<Integer, Function<Integer, Integer>> curried =
Transformations.<Integer, Integer, Integer>curryBiFunction().apply(add);
curried.apply(2).apply(3); // 5
Composition with explicit type info
Function.compose sometimes loses type inference when chained at the call site. higherCompose is the same operation but as a curried function value โ explicit type arguments fix it:
<T, U, V> Function<Function<U, V>, Function<Function<T, U>, Function<T, V>>> higherCompose();
Function<Integer, Integer> fx =
Transformations.<Integer, Integer, Integer>higherCompose()
.apply((Integer x) -> x + 2)
.apply((Integer x) -> x * 2);
fx.apply(3); // ((3*2)+2) = 8
Casting helpers โ assign a lambda to its target type
These helpers exist purely so you can hand a lambda to the compiler with an explicit target type, without writing the cast inline. Useful when type inference fails or when you want a one-liner local variable:
<T> Predicate<T> asPredicate(Predicate<T> p);
<T> Consumer<T> asConsumer(Consumer<T> c);
<T> Supplier<T> asSupplier(Supplier<T> s);
<T, R> Function<T, R> asFunc(Function<T, R> f);
<T, R> CheckedFunction<T, R> asCheckedFunc(Function<T, R> f);
var print = Transformations.<String>asConsumer(System.out::println);
var parse = Transformations.<String, Integer>asCheckedFunc(Integer::parseInt);
Stream sources
<T> Function<Iterator<T>, Stream<T>> iteratorToStream();
<T> Function<Enumeration<T>, Stream<T>> enumToStream();
Bridge non-Stream iteration APIs (e.g. ResultSet, JNDI enumerations, JDK 8 Iterators) into a Stream:
Stream<Locale> locales =
Transformations.<Locale>enumToStream().apply(someEnumeration);
Predicate negation
Function<Boolean, Boolean> not();
<T> Predicate<T> not(Predicate<T> p);
list.stream()
.filter(Transformations.not(String::isEmpty))
.forEach(System.out::println);
Related
- Checked Functions โ what the curried-checked variants return.
- Memoizer โ uses
curryBiFunction/curryTriFunctioninternally. - Stream Functions โ
streamFilter()lives in its own class.