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

control-types.mddocs/

Control Types

Monadic control structures for error handling, optional values, and validation that eliminate null pointer exceptions and provide functional composition patterns.

Capabilities

Option - Optional Values

Container for values that may or may not exist, eliminating null pointer exceptions with functional composition.

/**
 * Container for optional values - either Some(value) or None
 */
interface Option<T> extends Value<T> {
    // Factory methods
    static <T> Option<T> of(T value);          // Create Some(value) or None if value is null
    static <T> Option<T> some(T value);        // Create Some(value), throws if null
    static <T> Option<T> none();               // Create None instance
    static <T> Option<T> when(boolean condition, T value); // Conditional creation
    static <T> Option<T> when(boolean condition, Supplier<? extends T> supplier);
    
    // State checking
    boolean isDefined();                       // true if Some, false if None
    boolean isEmpty();                         // true if None, false if Some
    
    // Value access
    T get();                                   // Get value, throws if None
    T getOrElse(T other);                     // Get value or return other if None
    T getOrElse(Supplier<? extends T> supplier); // Get value or compute other if None
    <X extends Throwable> T getOrElseThrow(Supplier<X> exceptionSupplier) throws X;
    Option<T> orElse(Option<? extends T> other); // Return this if Some, other if None
    Option<T> orElse(Supplier<? extends Option<? extends T>> supplier);
    
    // Transformation operations
    <U> Option<U> map(Function<? super T, ? extends U> mapper);
    <U> Option<U> flatMap(Function<? super T, ? extends Option<? extends U>> mapper);
    Option<T> filter(Predicate<? super T> predicate);
    Option<T> filterNot(Predicate<? super T> predicate);
    
    // Side effects
    Option<T> peek(Consumer<? super T> action); // Perform action if Some
    
    // Conversion operations
    Either<Throwable, T> toEither();
    Try<T> toTry();
    List<T> toList();                         // Empty list if None, single-element if Some
    java.util.Optional<T> toJavaOptional();
    
    // Combining operations
    <U> Option<Tuple2<T, U>> zip(Option<? extends U> that);
    <U, R> Option<R> zipWith(Option<? extends U> that, BiFunction<? super T, ? super U, ? extends R> mapper);
}

/**
 * Some variant containing a value
 */
class Some<T> implements Option<T> {
    T get();                                   // Returns the contained value
}

/**
 * None variant representing absence of value
 */
class None<T> implements Option<T> {
    static None<Object> instance();            // Singleton None instance
}

Usage Examples:

import io.vavr.control.Option;

// Creating Options
Option<String> some = Option.of("Hello");
Option<String> none = Option.of(null);      // Creates None
Option<Integer> conditional = Option.when(5 > 3, 42); // Creates Some(42)

// Safe operations
String result = some
    .map(String::toUpperCase)
    .filter(s -> s.length() > 3)
    .getOrElse("DEFAULT");

// Chaining operations
Option<Integer> length = some.map(String::length);
Option<String> doubled = some.flatMap(s -> 
    s.isEmpty() ? Option.none() : Option.some(s + s));

// Pattern matching style
String describe = some.fold(
    () -> "No value",
    value -> "Value: " + value
);

// Converting to other types
java.util.Optional<String> javaOpt = some.toJavaOptional();
List<String> list = some.toList(); // [Hello] or empty list

// Combining Options
Option<String> firstName = Option.of("John");
Option<String> lastName = Option.of("Doe");
Option<String> fullName = firstName.zipWith(lastName, (f, l) -> f + " " + l);

Either - Disjoint Union

Represents a value that can be one of two types - typically used for error handling where Left represents error and Right represents success.

/**
 * Disjoint union type - either Left(error) or Right(value)
 */
interface Either<L, R> extends Value<R> {
    // Factory methods
    static <L, R> Either<L, R> left(L left);     // Create Left instance
    static <L, R> Either<L, R> right(R right);   // Create Right instance
    static <L, R> Either<L, R> cond(boolean test, R right, L left); // Conditional creation
    
    // State checking
    boolean isLeft();                          // true if Left, false if Right
    boolean isRight();                         // true if Right, false if Left
    
    // Value access
    R get();                                   // Get right value, throws if Left
    L getLeft();                              // Get left value, throws if Right
    R getOrElseGet(Function<? super L, ? extends R> other); // Get right or compute from left
    L getLeftOrElseGet(Function<? super R, ? extends L> other); // Get left or compute from right
    <X extends Throwable> R getOrElseThrow(Function<? super L, X> exceptionFunction) throws X;
    void orElseRun(Consumer<? super L> action); // Run action if Left
    
    // Alternative operations
    Either<L, R> orElse(Either<? extends L, ? extends R> other); // Use other if Left
    Either<L, R> orElse(Supplier<? extends Either<? extends L, ? extends R>> supplier);
    
    // Transformation operations
    <U> Either<L, U> map(Function<? super R, ? extends U> mapper);        // Transform right value
    <U> Either<U, R> mapLeft(Function<? super L, ? extends U> leftMapper); // Transform left value
    <L2, R2> Either<L2, R2> bimap(Function<? super L, ? extends L2> leftMapper,
                                  Function<? super R, ? extends R2> rightMapper);
    
    <U> Either<L, U> flatMap(Function<? super R, ? extends Either<L, ? extends U>> mapper);
    
    // Filtering
    Option<Either<L, R>> filter(Predicate<? super R> predicate);
    Option<Either<L, R>> filterOrElse(Predicate<? super R> predicate, L zero);
    
    // Side effects
    Either<L, R> peek(Consumer<? super R> action);        // Perform action if Right
    Either<L, R> peekLeft(Consumer<? super L> action);    // Perform action if Left
    
    // Swapping and projections
    Either<R, L> swap();                       // Swap Left and Right
    LeftProjection<L, R> left();              // Project to Left operations
    RightProjection<L, R> right();            // Project to Right operations
    
    // Folding operations
    <U> U fold(Function<? super L, ? extends U> leftMapper,
               Function<? super R, ? extends U> rightMapper);
    
    // Conversion operations
    Option<R> toOption();                      // Right becomes Some, Left becomes None
    Try<R> toTry();                           // Assumes Left contains Throwable
    Validation<L, R> toValidation();
}

/**
 * Left variant containing error/left value
 */
class Left<L, R> implements Either<L, R> {
    L getLeft();                              // Returns the left value
}

/**
 * Right variant containing success/right value  
 */
class Right<L, R> implements Either<L, R> {
    R get();                                  // Returns the right value
}

/**
 * Left projection for operating on left values
 */
class LeftProjection<L, R> {
    <U> Either<U, R> map(Function<? super L, ? extends U> mapper);
    <U> Either<U, R> flatMap(Function<? super L, ? extends Either<? extends U, R>> mapper);
    Option<L> filter(Predicate<? super L> predicate);
    L getOrElseGet(Function<? super R, ? extends L> other);
}

/**
 * Right projection for operating on right values
 */
class RightProjection<L, R> {
    <U> Either<L, U> map(Function<? super R, ? extends U> mapper);
    <U> Either<L, U> flatMap(Function<? super R, ? extends Either<L, ? extends U>> mapper);
    Option<R> filter(Predicate<? super R> predicate);
    R getOrElseGet(Function<? super L, ? extends R> other);
}

Usage Examples:

import io.vavr.control.Either;

// Creating Either instances
Either<String, Integer> success = Either.right(42);
Either<String, Integer> failure = Either.left("Error occurred");

// Error handling pattern
Either<String, String> result = parseInteger("123")
    .map(i -> "Parsed: " + i)
    .mapLeft(error -> "Failed: " + error);

// Chaining operations (right-biased)
Either<String, Integer> doubled = success
    .flatMap(value -> value > 0 ? Either.right(value * 2) : Either.left("Negative number"));

// Folding both sides
String message = result.fold(
    error -> "Error: " + error,
    value -> "Success: " + value
);

// Working with projections
String leftResult = failure.left().map(String::toUpperCase).getOrElseGet(i -> "No error");
Integer rightResult = success.right().map(i -> i + 1).getOrElseGet(err -> 0);

// Converting between types
Option<Integer> opt = success.toOption(); // Some(42) if Right, None if Left
Try<Integer> attempt = success.toTry();   // Success(42) if Right, Failure if Left

// Helper method example
static Either<String, Integer> parseInteger(String str) {
    try {
        return Either.right(Integer.parseInt(str));
    } catch (NumberFormatException e) {
        return Either.left("Invalid number: " + str);
    }
}

Try - Exception Handling

Represents a computation that may succeed with a value or fail with an exception, providing functional exception handling.

/**
 * Computation that may succeed or fail - either Success(value) or Failure(exception)
 */
interface Try<T> extends Value<T> {
    // Factory methods
    static <T> Try<T> of(CheckedFunction0<? extends T> supplier);     // Try computation
    static <T> Try<T> success(T value);                              // Create Success
    static <T> Try<T> failure(Throwable exception);                  // Create Failure
    static <T> Try<T> withResources(CheckedFunction0<? extends T> supplier,
                                   AutoCloseable... closeables);     // Try-with-resources
    
    // State checking
    boolean isSuccess();                       // true if Success, false if Failure
    boolean isFailure();                       // true if Failure, false if Success
    
    // Value access
    T get();                                   // Get value, throws if Failure
    T getOrElse(T other);                     // Get value or return other if Failure
    T getOrElse(Supplier<? extends T> supplier);
    <X extends Throwable> T getOrElseThrow(Function<? super Throwable, X> f) throws X;
    
    // Exception access (for Failure)
    Throwable getCause();                      // Get exception, throws if Success
    
    // Transformation operations
    <U> Try<U> map(Function<? super T, ? extends U> mapper);
    <U> Try<U> mapTry(CheckedFunction1<? super T, ? extends U> mapper);
    <U> Try<U> flatMap(Function<? super T, ? extends Try<? extends U>> mapper);
    <U> Try<U> flatMapTry(CheckedFunction1<? super T, ? extends Try<? extends U>> mapper);
    
    // Error handling
    Try<T> recover(Function<? super Throwable, ? extends T> recovery);
    Try<T> recoverWith(Function<? super Throwable, ? extends Try<? extends T>> recovery);
    Try<T> mapFailure(Function<? super Throwable, ? extends Throwable> f);
    
    // Filtering
    Try<T> filter(Predicate<? super T> predicate);
    Try<T> filterTry(CheckedPredicate<? super T> predicate);
    
    // Side effects
    Try<T> peek(Consumer<? super T> action);           // Perform action if Success
    Try<T> onFailure(Consumer<? super Throwable> action); // Perform action if Failure
    
    // Conversion operations
    Either<Throwable, T> toEither();           // Failure -> Left, Success -> Right
    Option<T> toOption();                      // Failure -> None, Success -> Some
    Validation<Throwable, T> toValidation();
    
    // Folding operations
    <U> U fold(Function<? super Throwable, ? extends U> failureMapper,
               Function<? super T, ? extends U> successMapper);
    
    // Combining operations
    <U> Try<Tuple2<T, U>> zip(Try<? extends U> that);
    <U, R> Try<R> zipWith(Try<? extends U> that, BiFunction<? super T, ? super U, ? extends R> mapper);
}

/**
 * Success variant containing the computed value
 */
class Success<T> implements Try<T> {
    T get();                                   // Returns the successful value
}

/**
 * Failure variant containing the exception
 */
class Failure<T> implements Try<T> {
    Throwable getCause();                      // Returns the exception
}

Usage Examples:

import io.vavr.control.Try;
import io.vavr.CheckedFunction1;

// Basic Try operations
Try<Integer> result = Try.of(() -> Integer.parseInt("123"));
Try<Integer> failure = Try.of(() -> Integer.parseInt("abc"));

// Chaining operations
Try<String> processed = result
    .map(i -> i * 2)
    .map(i -> "Result: " + i)
    .recover(ex -> "Error: " + ex.getMessage());

// Exception handling
Try<String> safe = Try.of(() -> riskyOperation())
    .recover(throwable -> "Default value")
    .map(String::toUpperCase);

// Filtering with exceptions
Try<Integer> positive = result.filter(i -> i > 0);  // Becomes Failure if <= 0

// Working with multiple Try instances
Try<String> name = Try.of(() -> getName());
Try<Integer> age = Try.of(() -> getAge());
Try<String> person = name.zipWith(age, (n, a) -> n + " is " + a + " years old");

// Try-with-resources pattern
Try<String> content = Try.withResources(
    () -> Files.lines(Paths.get("file.txt")).collect(Collectors.joining("\n")),
    Files.newBufferedReader(Paths.get("file.txt"))
);

// Converting to other types
Option<Integer> opt = result.toOption();     // Some(123) or None
Either<Throwable, Integer> either = result.toEither(); // Right(123) or Left(exception)

// Pattern matching style processing
String outcome = result.fold(
    exception -> "Failed with: " + exception.getMessage(),
    value -> "Succeeded with: " + value
);

// Helper methods with checked exceptions
static String riskyOperation() throws IOException {
    // Some operation that might throw
    return "success";
}

static CheckedFunction1<String, Integer> parseToInt = Integer::parseInt;

Validation - Accumulating Validation

Represents validation results that can accumulate multiple errors, useful for form validation and data processing.

/**
 * Validation that accumulates errors - either Valid(value) or Invalid(errors)
 */
interface Validation<E, T> extends Value<T> {
    // Factory methods
    static <E, T> Validation<E, T> valid(T value);        // Create Valid instance
    static <E, T> Validation<E, T> invalid(E error);      // Create Invalid with single error
    static <E, T> Validation<E, T> invalid(E... errors);  // Create Invalid with multiple errors
    static <E, T> Validation<E, T> invalid(Iterable<? extends E> errors);
    
    // Conditional creation
    static <E, T> Validation<E, T> fromTry(Try<? extends T> tryValue, Function<Throwable, E> errorMapper);
    static <E, T> Validation<E, T> fromEither(Either<E, ? extends T> either);
    static <E, T> Validation<E, T> fromOption(Option<? extends T> option, E ifNone);
    
    // State checking
    boolean isValid();                         // true if Valid, false if Invalid
    boolean isInvalid();                       // true if Invalid, false if Valid
    
    // Value access
    T get();                                   // Get value, throws if Invalid
    T getOrElse(T other);                     // Get value or return other if Invalid
    T getOrElse(Supplier<? extends T> supplier);
    
    // Error access
    Seq<E> getError();                        // Get errors, throws if Valid
    
    // Transformation operations
    <U> Validation<E, U> map(Function<? super T, ? extends U> mapper);
    <U> Validation<U, T> mapError(Function<E, U> errorMapper);
    <F, U> Validation<F, U> bimap(Function<E, F> errorMapper, Function<? super T, ? extends U> valueMapper);
    <U> Validation<E, U> flatMap(Function<? super T, ? extends Validation<E, ? extends U>> mapper);
    
    // Filtering
    Validation<E, T> filter(Predicate<? super T> predicate, E ifFalse);
    Validation<E, T> filter(Predicate<? super T> predicate, Supplier<? extends E> errorSupplier);
    
    // Combining validations (accumulates errors)
    <U> Validation<E, Tuple2<T, U>> zip(Validation<E, ? extends U> that);
    <U, R> Validation<E, R> zipWith(Validation<E, ? extends U> that, 
                                    BiFunction<? super T, ? super U, ? extends R> mapper);
    
    // Applicative operations for accumulating multiple validations
    static <E, T1, T2, R> Validation<E, R> combine(
        Validation<E, T1> v1, Validation<E, T2> v2,
        BiFunction<T1, T2, R> function);
    
    static <E, T1, T2, T3, R> Validation<E, R> combine(
        Validation<E, T1> v1, Validation<E, T2> v2, Validation<E, T3> v3,
        Function3<T1, T2, T3, R> function);
    // ... up to combine8
    
    // Folding operations
    <U> U fold(Function<? super Seq<E>, ? extends U> invalidMapper,
               Function<? super T, ? extends U> validMapper);
    
    // Conversion operations
    Either<Seq<E>, T> toEither();              // Invalid -> Left(errors), Valid -> Right(value)
    Option<T> toOption();                      // Invalid -> None, Valid -> Some(value)
    Try<T> toTry();                           // Invalid -> Failure, Valid -> Success
}

/**
 * Valid variant containing the validated value
 */
class Valid<E, T> implements Validation<E, T> {
    T get();                                   // Returns the valid value
}

/**
 * Invalid variant containing accumulated errors
 */
class Invalid<E, T> implements Validation<E, T> {
    Seq<E> getError();                        // Returns the accumulated errors
}

Usage Examples:

import io.vavr.control.Validation;
import io.vavr.collection.List;
import static io.vavr.control.Validation.*;

// Form validation example
public class Person {
    private final String name;
    private final String email;
    private final int age;
    
    public Person(String name, String email, int age) {
        this.name = name;
        this.email = email;
        this.age = age;
    }
}

// Individual field validations
static Validation<String, String> validateName(String name) {
    return name != null && name.trim().length() >= 2 
        ? valid(name.trim())
        : invalid("Name must be at least 2 characters");
}

static Validation<String, String> validateEmail(String email) {
    return email != null && email.contains("@")
        ? valid(email)
        : invalid("Email must contain @");
}

static Validation<String, Integer> validateAge(Integer age) {
    return age != null && age >= 0 && age <= 150
        ? valid(age)
        : invalid("Age must be between 0 and 150");
}

// Combining validations (accumulates all errors)
static Validation<List<String>, Person> validatePerson(String name, String email, Integer age) {
    return Validation.combine(
        validateName(name).mapError(List::of),
        validateEmail(email).mapError(List::of),
        validateAge(age).mapError(List::of)
    ).ap((n, e, a) -> new Person(n, e, a));
}

// Usage
Validation<List<String>, Person> result = validatePerson("", "invalid-email", -5);
// result.isInvalid() == true
// result.getError() == ["Name must be at least 2 characters", "Email must contain @", "Age must be between 0 and 150"]

// Successful validation
Validation<List<String>, Person> success = validatePerson("John", "john@example.com", 30);
// success.isValid() == true
// success.get() == Person("John", "john@example.com", 30)

// Working with validation results
String message = result.fold(
    errors -> "Validation failed: " + errors.mkString(", "),
    person -> "Valid person: " + person
);

// Converting to other types
Option<Person> maybePerson = success.toOption();
Either<List<String>, Person> eitherPerson = result.toEither();

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