CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-vavr--vavr

Object-functional language extension to Java 8+ providing persistent collections, functional abstractions, and monadic control types.

Pending
Overview
Eval results
Files

functional-interfaces.mddocs/

Functional Interfaces

Extended function interfaces with composition, memoization, and exception handling capabilities that enhance Java's standard functional interfaces.

Capabilities

Function 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 arities

Usage 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)

Checked Function Interfaces

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 CheckedFunction8

Usage 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");

Predicate and Consumer Interfaces

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();

Partial Functions

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

docs

collections.md

concurrent.md

control-types.md

core-types.md

functional-interfaces.md

index.md

tile.json