Object-functional language extension to Java 8+ providing persistent collections, functional abstractions, and monadic control types.
—
Extended function interfaces with composition, memoization, and exception handling capabilities that enhance Java's standard functional interfaces.
Extended function interfaces from arity 0 to 8 with additional methods for composition, memoization, and currying.
/**
* Supplier function (0 arguments)
*/
@FunctionalInterface
interface Function0<R> extends Supplier<R> {
// Factory methods
static <R> Function0<R> of(Function0<R> function);
static <R> Function0<R> constant(R value); // Function that always returns value
// Execution
R apply(); // Execute the function
// Memoization
Function0<R> memoized(); // Cache result after first call
boolean isMemoized(); // Check if this function is memoized
// Composition
default <V> Function0<V> andThen(Function<? super R, ? extends V> after);
// Lifting
static <R> Function1<Integer, R> lift(Function0<R> function); // Lift to Function1 ignoring argument
}
/**
* Single argument function
*/
@FunctionalInterface
interface Function1<T1, R> extends Function<T1, R> {
// Factory methods
static <T1, R> Function1<T1, R> of(Function1<T1, R> function);
static <T1, R> Function1<T1, R> identity(); // Function returning its argument
static <T1, R> Function1<T1, R> constant(R value); // Function ignoring argument, returning value
// Execution
R apply(T1 t1); // Apply function to argument
// Memoization
Function1<T1, R> memoized(); // Cache results by argument
boolean isMemoized();
// Composition
default <V> Function1<T1, V> andThen(Function<? super R, ? extends V> after);
default <V> Function1<V, R> compose(Function<? super V, ? extends T1> before);
// Currying (for compatibility - Function1 is already curried)
Function1<T1, R> curried();
// Partial application
Function0<R> apply(T1 t1); // Partially apply argument, return Function0
// Lifting operations
static <T1, R> Function1<Option<T1>, Option<R>> lift(Function1<T1, R> function);
static <T1, R> Function1<Try<T1>, Try<R>> liftTry(Function1<T1, R> function);
}
/**
* Two argument function (BiFunction)
*/
@FunctionalInterface
interface Function2<T1, T2, R> extends BiFunction<T1, T2, R> {
// Factory methods
static <T1, T2, R> Function2<T1, T2, R> of(Function2<T1, T2, R> function);
static <T1, T2, R> Function2<T1, T2, R> constant(R value);
// Execution
R apply(T1 t1, T2 t2); // Apply function to arguments
// Memoization
Function2<T1, T2, R> memoized(); // Cache results by argument tuple
boolean isMemoized();
// Composition
default <V> Function2<T1, T2, V> andThen(Function<? super R, ? extends V> after);
// Currying
Function1<T1, Function1<T2, R>> curried(); // Convert to curried form
// Partial application
Function1<T2, R> apply(T1 t1); // Apply first argument
Function0<R> apply(T1 t1, T2 t2); // Apply both arguments
// Argument manipulation
Function2<T2, T1, R> reversed(); // Swap argument order
// Lifting operations
static <T1, T2, R> Function2<Option<T1>, Option<T2>, Option<R>> lift(Function2<T1, T2, R> function);
}
/**
* Three argument function
*/
@FunctionalInterface
interface Function3<T1, T2, T3, R> {
// Factory methods
static <T1, T2, T3, R> Function3<T1, T2, T3, R> of(Function3<T1, T2, T3, R> function);
static <T1, T2, T3, R> Function3<T1, T2, T3, R> constant(R value);
// Execution
R apply(T1 t1, T2 t2, T3 t3); // Apply function to arguments
// Memoization
Function3<T1, T2, T3, R> memoized();
boolean isMemoized();
// Composition
default <V> Function3<T1, T2, T3, V> andThen(Function<? super R, ? extends V> after);
// Currying
Function1<T1, Function1<T2, Function1<T3, R>>> curried(); // Convert to curried form
// Partial application
Function2<T2, T3, R> apply(T1 t1); // Apply first argument
Function1<T3, R> apply(T1 t1, T2 t2); // Apply first two arguments
Function0<R> apply(T1 t1, T2 t2, T3 t3); // Apply all arguments
}
// ... continues similarly for Function4 through Function8 with appropriate aritiesUsage Examples:
import io.vavr.Function1;
import io.vavr.Function2;
import io.vavr.Function3;
// Creating functions
Function1<String, Integer> length = String::length;
Function2<Integer, Integer, Integer> add = Integer::sum;
Function3<Integer, Integer, Integer, Integer> add3 = (a, b, c) -> a + b + c;
// Function composition
Function1<String, String> toUpper = String::toUpperCase;
Function1<String, Integer> upperLength = length.compose(toUpper);
Function1<String, String> describe = length.andThen(len -> "Length: " + len);
// Memoization (caching results)
Function1<Integer, Integer> expensive = n -> {
System.out.println("Computing for " + n);
return n * n;
};
Function1<Integer, Integer> cached = expensive.memoized();
cached.apply(5); // Prints "Computing for 5", returns 25
cached.apply(5); // Returns 25 without printing (cached)
// Currying
Function2<Integer, Integer, Integer> multiply = (a, b) -> a * b;
Function1<Integer, Function1<Integer, Integer>> curried = multiply.curried();
Function1<Integer, Integer> multiplyBy5 = curried.apply(5);
Integer result = multiplyBy5.apply(10); // 50
// Partial application
Function3<String, String, String, String> concat3 = (a, b, c) -> a + b + c;
Function2<String, String, String> prefixed = concat3.apply("Hello ");
Function1<String, String> greeting = prefixed.apply("World");
String message = greeting.apply("!"); // "Hello World!"
// Lifting functions to work with containers
Function1<String, Integer> safeLength = Function1.lift(String::length);
Option<String> maybeName = Option.of("John");
Option<Integer> maybeLength = safeLength.apply(maybeName); // Some(4)
// Working with Try
Function1<String, Integer> parseToInt = Integer::parseInt;
Function1<Try<String>, Try<Integer>> safeParse = Function1.liftTry(parseToInt);
Try<String> input = Try.of(() -> "123");
Try<Integer> parsed = safeParse.apply(input); // Success(123)Function interfaces that can throw checked exceptions, providing functional programming with exception handling.
/**
* Supplier that may throw checked exceptions
*/
@FunctionalInterface
interface CheckedFunction0<R> {
// Execution
R apply() throws Throwable; // Execute function, may throw
// Conversion to regular functions
default Function0<Try<R>> unchecked(); // Convert to Function0 returning Try
default Function0<R> recover(Function<? super Throwable, ? extends R> recover);
// Lifting
static <R> CheckedFunction0<R> of(CheckedFunction0<R> function);
static <R> CheckedFunction0<R> constant(R value); // Function returning constant value
// Memoization
CheckedFunction0<R> memoized();
}
/**
* Single argument function that may throw checked exceptions
*/
@FunctionalInterface
interface CheckedFunction1<T1, R> {
// Execution
R apply(T1 t1) throws Throwable; // Apply function, may throw
// Conversion to regular functions
default Function1<T1, Try<R>> unchecked(); // Convert to Function1 returning Try
default Function1<T1, R> recover(Function<? super Throwable, ? extends R> recover);
// Lifting
static <T1, R> CheckedFunction1<T1, R> of(CheckedFunction1<T1, R> function);
static <T1, R> CheckedFunction1<T1, R> identity();
static <T1, R> CheckedFunction1<T1, R> constant(R value);
// Composition (with exception handling)
default <V> CheckedFunction1<T1, V> andThen(CheckedFunction1<? super R, ? extends V> after);
default <V> CheckedFunction1<V, R> compose(CheckedFunction1<? super V, ? extends T1> before);
// Memoization
CheckedFunction1<T1, R> memoized();
}
/**
* Two argument function that may throw checked exceptions
*/
@FunctionalInterface
interface CheckedFunction2<T1, T2, R> {
// Execution
R apply(T1 t1, T2 t2) throws Throwable; // Apply function, may throw
// Conversion to regular functions
default Function2<T1, T2, Try<R>> unchecked(); // Convert to Function2 returning Try
default Function2<T1, T2, R> recover(Function<? super Throwable, ? extends R> recover);
// Lifting and factory methods
static <T1, T2, R> CheckedFunction2<T1, T2, R> of(CheckedFunction2<T1, T2, R> function);
static <T1, T2, R> CheckedFunction2<T1, T2, R> constant(R value);
// Composition
default <V> CheckedFunction2<T1, T2, V> andThen(CheckedFunction1<? super R, ? extends V> after);
// Currying
CheckedFunction1<T1, CheckedFunction1<T2, R>> curried();
// Partial application
CheckedFunction1<T2, R> apply(T1 t1);
// Memoization
CheckedFunction2<T1, T2, R> memoized();
}
// ... continues for CheckedFunction3 through CheckedFunction8Usage Examples:
import io.vavr.CheckedFunction1;
import io.vavr.CheckedFunction2;
import io.vavr.control.Try;
// Checked functions for operations that may throw
CheckedFunction1<String, Integer> parseInteger = Integer::parseInt;
CheckedFunction1<String, String> readFile = fileName -> Files.readString(Paths.get(fileName));
CheckedFunction2<String, String, Void> writeFile = (fileName, content) -> {
Files.writeString(Paths.get(fileName), content);
return null;
};
// Converting to safe functions
Function1<String, Try<Integer>> safeParse = parseInteger.unchecked();
Try<Integer> result1 = safeParse.apply("123"); // Success(123)
Try<Integer> result2 = safeParse.apply("abc"); // Failure(NumberFormatException)
// Using recover for error handling
Function1<String, Integer> parseWithDefault = parseInteger.recover(ex -> -1);
Integer parsed1 = parseWithDefault.apply("123"); // 123
Integer parsed2 = parseWithDefault.apply("abc"); // -1
// Composition with checked functions
CheckedFunction1<String, String> processFile = readFile
.andThen(content -> content.toUpperCase())
.andThen(upper -> upper.replace("OLD", "NEW"));
Try<String> processed = processFile.unchecked().apply("input.txt");
// Memoized checked functions
CheckedFunction1<String, String> expensiveOperation = input -> {
Thread.sleep(1000); // Simulate expensive operation
return "Processed: " + input;
};
CheckedFunction1<String, String> memoized = expensiveOperation.memoized();
// First call - slow
Try<String> result3 = memoized.unchecked().apply("test");
// Second call - fast (cached)
Try<String> result4 = memoized.unchecked().apply("test");Enhanced predicate and consumer interfaces with exception handling and composition.
/**
* Predicate that may throw checked exceptions
*/
@FunctionalInterface
interface CheckedPredicate<T> {
// Execution
boolean test(T t) throws Throwable; // Test predicate, may throw
// Conversion to regular predicates
default Predicate<T> unchecked(); // Convert to Predicate catching exceptions
// Logical operations
default CheckedPredicate<T> and(CheckedPredicate<? super T> other);
default CheckedPredicate<T> or(CheckedPredicate<? super T> other);
default CheckedPredicate<T> negate();
// Factory methods
static <T> CheckedPredicate<T> of(CheckedPredicate<T> predicate);
}
/**
* Consumer that may throw checked exceptions
*/
@FunctionalInterface
interface CheckedConsumer<T> {
// Execution
void accept(T t) throws Throwable; // Consume value, may throw
// Conversion to regular consumer
default Consumer<T> unchecked(); // Convert to Consumer catching exceptions
// Chaining
default CheckedConsumer<T> andThen(CheckedConsumer<? super T> after);
// Factory methods
static <T> CheckedConsumer<T> of(CheckedConsumer<T> consumer);
}
/**
* Runnable that may throw checked exceptions
*/
@FunctionalInterface
interface CheckedRunnable {
// Execution
void run() throws Throwable; // Run operation, may throw
// Conversion to regular Runnable
default Runnable unchecked(); // Convert to Runnable catching exceptions
// Factory methods
static CheckedRunnable of(CheckedRunnable runnable);
}Usage Examples:
import io.vavr.CheckedPredicate;
import io.vavr.CheckedConsumer;
import io.vavr.CheckedRunnable;
// Checked predicates
CheckedPredicate<String> isValidFile = fileName -> Files.exists(Paths.get(fileName));
CheckedPredicate<String> isReadable = fileName -> Files.isReadable(Paths.get(fileName));
// Combining predicates
CheckedPredicate<String> canRead = isValidFile.and(isReadable);
Predicate<String> safeCanRead = canRead.unchecked(); // Won't throw, returns false on exception
// Using in streams
List<String> validFiles = fileNames.filter(safeCanRead);
// Checked consumers
CheckedConsumer<String> writeToLog = message -> {
Files.write(Paths.get("app.log"), (message + "\n").getBytes(),
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
};
CheckedConsumer<String> sendEmail = message -> emailService.send(message);
// Chaining consumers
CheckedConsumer<String> logAndEmail = writeToLog.andThen(sendEmail);
Consumer<String> safeProcess = logAndEmail.unchecked();
// Using with collections
messages.forEach(safeProcess);
// Checked runnable
CheckedRunnable startup = () -> {
initializeDatabase();
loadConfiguration();
startServices();
};
// Convert to safe runnable
Runnable safeStartup = startup.unchecked();
new Thread(safeStartup).start();Functions that are not defined for all possible inputs, providing pattern matching and domain restriction capabilities.
/**
* Function that is not defined for all inputs
*/
interface PartialFunction<T, R> extends Function1<T, R> {
// Factory methods
static <T, R> PartialFunction<T, R> of(Predicate<? super T> isDefinedAt,
Function<? super T, ? extends R> apply);
// Domain checking
boolean isDefinedAt(T value); // Check if function is defined for value
// Safe application
Option<R> lift(T value); // Apply if defined, return None otherwise
// Execution (throws if not defined)
R apply(T value); // Apply function, throws if not defined at value
// Composition
default <V> PartialFunction<T, V> andThen(Function<? super R, ? extends V> after);
default <V> PartialFunction<V, R> compose(PartialFunction<? super V, ? extends T> before);
// Combination with other partial functions
default PartialFunction<T, R> orElse(PartialFunction<? super T, ? extends R> that);
// Lifting to total function
default Function1<T, Option<R>> lift(); // Convert to total function returning Option
// Pattern matching support
static <T, R> PartialFunction<T, R> caseOf(Predicate<? super T> condition,
Function<? super T, ? extends R> function);
}Usage Examples:
import io.vavr.PartialFunction;
import io.vavr.control.Option;
// Creating partial functions
PartialFunction<Integer, String> evenToString = PartialFunction.of(
n -> n % 2 == 0, // Defined only for even numbers
n -> "Even: " + n // Function to apply
);
PartialFunction<Integer, String> oddToString = PartialFunction.of(
n -> n % 2 == 1, // Defined only for odd numbers
n -> "Odd: " + n // Function to apply
);
// Checking if defined
boolean defined = evenToString.isDefinedAt(4); // true
boolean notDefined = evenToString.isDefinedAt(3); // false
// Safe application
Option<String> result1 = evenToString.lift(4); // Some("Even: 4")
Option<String> result2 = evenToString.lift(3); // None
// Combining partial functions
PartialFunction<Integer, String> numberToString = evenToString.orElse(oddToString);
// Now defined for all integers
String result3 = numberToString.apply(4); // "Even: 4"
String result4 = numberToString.apply(3); // "Odd: 3"
// Pattern matching style
PartialFunction<Object, String> classifier = PartialFunction
.caseOf(String.class::isInstance, obj -> "String: " + obj)
.orElse(PartialFunction.caseOf(Integer.class::isInstance, obj -> "Integer: " + obj))
.orElse(PartialFunction.caseOf(o -> true, obj -> "Other: " + obj.getClass().getSimpleName()));
String classification1 = classifier.apply("hello"); // "String: hello"
String classification2 = classifier.apply(42); // "Integer: 42"
String classification3 = classifier.apply(3.14); // "Other: Double"
// Converting to total function
Function1<Integer, Option<String>> totalFunction = evenToString.lift();
Option<String> maybeResult = totalFunction.apply(5); // None
// Using in collection processing
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
List<String> evenStrings = numbers
.map(evenToString.lift()) // Apply partial function safely
.filter(Option::isDefined) // Keep only defined results
.map(Option::get); // Extract values
// Result: ["Even: 2", "Even: 4", "Even: 6"]Install with Tessl CLI
npx tessl i tessl/maven-io-vavr--vavr