Interface Validated<E,T>

All Known Implementing Classes:
Validated.Invalid, Validated.Valid

public sealed interface Validated<E,T> permits Validated.Valid<E,T>, Validated.Invalid<E,T>
Like Result, but the Invalid case accumulates ALL errors instead of short-circuiting on the first one.

Pick Validated over Result when you want every reason a form, config, or DTO is invalid, not just the first.

Validated<String, Form> v = nameField.zip(emailField, Form::new);
// If both invalid: v carries both error messages.

Convert to/from Result when you need to switch behaviour mid-pipeline.

  • Method Details

    • valid

      static <E,T> Validated<E,T> valid(T value)
      Build a success.
    • invalid

      static <E,T> Validated<E,T> invalid(E error)
      Build a failure carrying a single error.
    • invalid

      static <E,T> Validated<E,T> invalid(NonEmptyList<E> errors)
      Build a failure carrying one or more accumulated errors.
    • fromResult

      static <E,T> Validated<E,T> fromResult(Result<E,T> r)
      Lift a Result into a single-error Validated.
    • traverse

      static <E,T,R> Validated<E,List<R>> traverse(List<? extends T> items, Function<? super T, ? extends Validated<E,R>> parse)
      Validate every element of a list with one function, accumulating every failure: the accumulative "traverse". Validated.Valid of the parsed elements (in input order) only if every element passed; otherwise Validated.Invalid carrying the errors of every failed element, in input order.

      An empty list is trivially Valid of an empty list. When emptiness is itself a failure, pair with NonEmptyList.fromList, or use the NonEmptyList overload, which carries non-emptiness through: non-empty in, non-empty out.

    • traverse

      static <E,T,R> Validated<E, NonEmptyList<R>> traverse(NonEmptyList<T> items, Function<? super T, ? extends Validated<E,R>> parse)
      traverse, preserving non-emptiness.
    • accumulate

      static <E,T> Validated<E,T> accumulate(Function<? super Validated.Accumulator<E>, ? extends T> block)
      Error-accumulation DSL: validate several independent pieces as straight-line code and surface every failure at once. The Validated counterpart of Result.binding, and the arity-free alternative to chained zip calls.

      Inside the block, acc.on(...) binds each validation and returns a Validated.Bound handle. A failed binding records its errors and keeps going, so every validation runs. Unwrap the handles with Validated.Bound.value() once everything is bound; the first unwrap of a failed binding ends the block. The result is Validated.Valid of the block's return value only if no binding failed, otherwise Validated.Invalid with all errors in binding order:

      Validated<Failure, Form> form = Validated.accumulate(acc -> {
          var name  = acc.on(validateName(raw));    // Invalid -> recorded, no abort
          var email = acc.on(validateEmail(raw));   // still runs
          var age   = acc.on(validateAge(raw));     // still runs
          return new Form(name.value(), email.value(), age.value());
      });
      // form == Invalid([nameError, emailError, ageError]) if all three failed
      

      Bind first, unwrap last. Interleaving (acc.on(a).value() before binding b) silently degrades to short-circuiting: a failed unwrap aborts before later validations bind. Dependent validations belong in Result.binding; this DSL is for independent ones.

      The abort uses the same private control-flow exception mechanism as Result.binding (see ADR-1), with the same caveats: don't wrap unwraps in a catch-all catch (RuntimeException e), and don't let Bound handles escape the block. Nested accumulate blocks are safe: each abort carries the identity of the block that created it and unwinds to that block's boundary.

      Parameters:
      block - receives the Validated.Accumulator and returns the composed value.
      Returns:
      Validated.Valid of the block's return value, or Validated.Invalid carrying all accumulated errors in binding order.
    • isValid

      default boolean isValid()
      true if this is Validated.Valid.
    • isInvalid

      default boolean isInvalid()
      true if this is Validated.Invalid.
    • map

      default <U> Validated<E,U> map(Function<? super T, ? extends U> f)
      Transform the success value; preserve the errors.
    • mapErr

      default <F> Validated<F,T> mapErr(Function<? super E, ? extends F> f)
      Transform every error in the batch; preserve the valid value. The accumulating counterpart of Result.mapErr, typically used at the boundary to translate domain errors into API- or user-facing shapes without losing any of the batch.
    • zip

      default <U,R> Validated<E,R> zip(Validated<E,U> other, BiFunction<? super T, ? super U, ? extends R> f)
      Combine two Validateds into one via a binary function.

      Error semantics (the key difference from Result):

      This is the ap ("applicative") combinator. Use to validate independent fields together and surface all problems at once.

    • toResult

      default Result<NonEmptyList<E>, T> toResult()
      Convert back into a Result. The error side becomes the accumulated list so the caller can pattern-match on either a single error or a batch.
    • fold

      default <R> R fold(Function<? super NonEmptyList<E>, ? extends R> onInvalid, Function<? super T, ? extends R> onValid)
      Fold both cases into a single value.