Validated represents either a successful value or a failed computation. Unlike Either, combining multiple failed computations preserves all their errors instead of stopping at one failure. section 4.6
A Validated[E, A] value has a successful Valid case and an Invalid case containing errors. Naming this behavior gives error accumulation a reusable home rather than scattering it across separate -All functions. section 4.4.2
The core operations make the accumulation rule explicit:
- map transforms a successful value and preserves an existing Invalid value. section 4.4.2
- map2 combines two successful values, propagates a single failure, or combines both failures. section 4.4.2
- traverse and sequence apply the same rule across lists of computations. section 4.4.2
Combining failures
Combining Invalid(es1) with Invalid(es2) produces Invalid(es1 ++ es2). A generalized Validated replaces List[E] with an error type E and accepts combineErrors: (E, E) => E, allowing errors to use another structure. section 4.4.2
enum Validated[+E, +A]:
case Valid(get: A)
case Invalid(errors: List[E])
def toEither: Either[List[E], A] =
this match
case Valid(a) => Either.Right(a)
case Invalid(es) => Either.Left(es)
def map[B](f: A => B): Validated[E, B] =
this match
case Valid(a) => Valid(f(a))
case Invalid(es) => Invalid(es)
def map2[EE >: E, B, C](
b: Validated[EE, B])(
f: (A, B) => C
): Validated[EE, C] =
(this, b) match
case (Valid(aa), Valid(bb)) => Valid(f(aa, bb))
case (Invalid(es), Valid(_)) => Invalid(es)
case (Valid(_), Invalid(es)) => Invalid(es)
case (Invalid(es1), Invalid(es2)) => Invalid(es1 ++ es2)
object Validated:
def fromEither[E, A](e: Either[List[E], A]): Validated[E, A] =
e match
case Either.Right(a) => Valid(a)
case Either.Left(es) => Invalid(es)
def traverse[E, A, B](
as: List[A], f: A => Validated[E, B]
): Validated[E, List[B]] =
as.foldRight(Valid(Nil): Validated[E, List[B]])((a, acc) =>
f(a).map2(acc)(_ :: _))
def sequence[E, A](vs: List[Validated[E, A]]): Validated[E, List[A]] =
traverse(vs, identity)
Inspect the complete data type and its conversion, mapping, combination, and traversal operations. section 4.4.2
