validation

Result

Related Docs: object Result | package validation

sealed trait Result[+E, +A] extends Product with Serializable

Represents a value of one of two states:

It is similar to Either or a more generic Try.

Motivation

Scalas Either is unbiased, meaning it treats its two sides equally and requires one to explicitly state on which side one wants to work on. More often that not, one side is representing the error or invalid part and one side representing the success or valid part.

Result sets the invalid side on the left and the valid side on the right and assumes, that one wants to primarily work on the right/valid side. This is similar to Try, which fixes the invalid type to Throwable.

Example:

import validation._, Result._

case class Person(name: String)

def validateName(input: String): Result[String, String] =
  if (input.trim.isEmpty)
    invalid("name must not be empty")
  else
    valid(input)

List("Bernd", "").map(validateName(_).map(Person.apply))

// List[Result[String,Person]] = List(
//   Valid(Person(Bernd)),
//   Invalid(name must not be empty)
// )

Both, Either and Try, cannot accumulate errors, but are typically aborting with the first failure, making them disadvantageous for parsing and validating data on system boundaries. For example, parsing a JSON data structure can lead to multiple issues and it is good measure to report as many errors as you can back to the user. With Either or Try, only one error at the time gets reported, leading to the user to submit a fixed version only to encounter, that they have to fixed yet another error.

Result can accumulate errors and thus allows to report as many of them as possible. There are multiple ways to combine two or more Results, for a detailed description, see their respective documentation.

Example:

import validation._, Result._

case class Person(name: String, age: Int)

def validateName(input: String): Result[String, String] =
  if (input.trim.isEmpty)
    invalid("name must not be empty")
  else
    valid(input)

def validateAge(input: String): Result[String, Int] = {
  val ageVal =
    if (input.trim.isEmpty)
      invalid("age must not be empty")
    else
      Result.parseInt(input).invalidMap(_.getMessage)
  ageVal.filter(_ >= 0, "age must be positive")
}

def parsePerson(inName: String, inAge: String): Result[List[String], Person] = {
  val nameVal = validateName(inName).invalidMap(List(_))
  val ageVal = validateAge(inAge).invalidMap(List(_))
  (nameVal and ageVal) apply Person
}

val inputs = List(
  ("Bernd", "42"),
  ("Ralle", ""),
  ("Ronny", "foo"),
  ("", "-1337")
)

inputs.map((parsePerson _).tupled)

// List[Result[List[String],Person]] = List(
//   Valid(Person(Bernd,42)),
//   Invalid(List(age must not be empty)),
//   Invalid(List(For input string: "foo")),
//   Invalid(List(name must not be empty, age must be positive))
// )
E

the Invalid type

A

the Valid type

Since

0.1.0

Linear Supertypes
Serializable, Serializable, Product, Equals, Any
Known Subclasses
Type Hierarchy Learn more about scaladoc diagrams
Ordering
  1. Grouped
  2. Alphabetic
  3. By inheritance
Inherited
  1. Result
  2. Serializable
  3. Serializable
  4. Product
  5. Equals
  6. Any
  1. Hide All
  2. Show all
Learn more about member selection
Visibility
  1. Public
  2. All

Abstract Value Members

  1. abstract def canEqual(that: Any): Boolean

    Definition Classes
    Equals
  2. abstract def getClass(): Class[_]

    Definition Classes
    Any
  3. abstract def productArity: Int

    Definition Classes
    Product
  4. abstract def productElement(n: Int): Any

    Definition Classes
    Product

Concrete Value Members

  1. final def !=(arg0: Any): Boolean

    Definition Classes
    Any
  2. final def ##(): Int

    Definition Classes
    Any
  3. def ==(other: Result[E, A]): Boolean

    [use case]

    [use case]
    other

    returns

    Full Signature

    def ==[EE >: E, AA >: A](other: Result[EE, AA])(implicit EE: Equiv[EE], AA: Equiv[AA]): Boolean

  4. final def ==(arg0: Any): Boolean

    Definition Classes
    Any
  5. def and[B](other: Result[E, B]): Ap2[E, A, B]

    [use case] Combines two Results in a builder, eventually accumulating invalids.

    [use case]

    Combines two Results in a builder, eventually accumulating invalids.

    This is the Applicative Builder of the valid result.

    B

    the other valid type

    other

    the other Result this one should be combined with

    returns

    a builder to eventually apply two Results

    Full Signature

    def and[EE >: E, AA >: A, B](other: Result[EE, B]): Ap2[EE, AA, B]

  6. def append(other: Result[E, A]): Result[E, A]

    [use case]

    [use case]
    other

    returns

    Full Signature

    def append[EE >: E, AA >: A](other: ⇒ Result[EE, AA]): Result[EE, NonEmptyVector[AA]]

  7. def apply[B](f: Result[E, (A) ⇒ B]): Result[E, B]

    [use case] Applies a function in the Result context to the valid value, accumulating invalids.

    [use case]

    Applies a function in the Result context to the valid value, accumulating invalids.

    This is the Applicative Functor of the valid Result.

    B

    the resulting valid type

    f

    the function in the Result context

    returns

    the valid result if this and f are valid or invalid if one of each is invalid or both invalid values accumulated

    Full Signature

    def apply[EE >: E, B](f: Result[EE, (A) ⇒ B]): Result[EE, B]

  8. def as[B](x: ⇒ B): Result[E, B]

    Sets the valid value to x and discards the previous value if this is a valid Result.

    Sets the valid value to x and discards the previous value if this is a valid Result.

    B

    the resulting valid type

    x

    the new valid value

    returns

    the result with the valid value set to x

    Since

    0.2.0

  9. final def asInstanceOf[T0]: T0

    Definition Classes
    Any
  10. def bimap[EE, AA](fe: (E) ⇒ EE, fa: (A) ⇒ AA): Result[EE, AA]

    Transforms the value of this Result.

    Transforms the value of this Result.

    EE

    the resulting invalid type

    AA

    the resulting valid type

    fe

    the function to transform an invalid value

    fa

    the function to transform a valid value

    returns

    the result with either the valid or invalid value transformed

  11. def compare(other: Result[E, A]): Int

    [use case]

    [use case]
    other

    returns

    Full Signature

    def compare[EE >: E, AA >: A](other: Result[EE, AA])(implicit EE: Ordering[EE], AA: Ordering[AA]): Int

  12. def contains(x: A): Boolean

    [use case] Tests for membership on the valid Result

    [use case]

    Tests for membership on the valid Result

    x

    returns

    true if this Result has x as valid value

    Full Signature

    def contains[AA >: A](x: ⇒ AA): Boolean

  13. def equals(arg0: Any): Boolean

    Definition Classes
    Any
  14. def exists(p: (A) ⇒ Boolean): Boolean

    p
    returns

    true if this Result is valid and satisfies the predicate p

  15. def filter(p: (A) ⇒ Boolean, ifEmpty: ⇒ E): Result[E, A]

    [use case] Filters the valid value of this Result.

    [use case]

    Filters the valid value of this Result.

    p

    ifEmpty

    returns

    Full Signature

    def filter[EE >: E](p: (A) ⇒ Boolean, ifEmpty: ⇒ EE): Result[EE, A]

  16. def flatMap[B](f: (A) ⇒ Result[E, B]): Result[E, B]

    [use case] Continues validation with the provided function if this is a valid Result.

    [use case]

    Continues validation with the provided function if this is a valid Result.

    flatMap does not accumulate errors. If you want to do so, use and instead.

    This is the Monadic Bind through the valid value.

    B

    the resulting valid type

    f

    the function to continue with

    returns

    the result of applying f over the valid value in the Result context

    Full Signature

    def flatMap[EE >: E, B](f: (A) ⇒ Result[EE, B]): Result[EE, B]

  17. def fold[B](fe: (NonEmptyVector[E]) ⇒ B, fa: (A) ⇒ B): B

    Folds this Result into a value by applying the first function if this is a Invalid or the second function if this is a Valid.

    Folds this Result into a value by applying the first function if this is a Invalid or the second function if this is a Valid.

    This is the Catamorphism.

    B

    the resulting type

    fe

    the function to apply in the invalid case

    fa

    the function to apply in the valid case

    returns

    the result of one of the two functions

  18. def foldLeft[B](b: B)(f: (B, A) ⇒ B): B

    A left fold over the valid value.

    A left fold over the valid value.

    B

    the resulting type

    b

    the initial accumulator

    f

    reducer function of the accumulator and the valid value

    returns

    b if this is Invalid, otherwise the result of applying f

    See also

    fold

  19. def foldRight[B](b: B)(f: (A, B) ⇒ B): B

    A right fold over the valid value.

    A right fold over the valid value.

    B

    the resulting type

    b

    the initial accumulator

    f

    reducer function of the valid value and the accumulator

    returns

    b if this is Invalid, otherwise the result of applying f

    See also

    fold

  20. def forall(p: (A) ⇒ Boolean): Boolean

    p
    returns

    true if this Result is invalid or the valid value satisfies the predicate p

  21. def getEither: A

    [use case]

    [use case]
    returns

    Full Signature

    def getEither[AA >: A](implicit ev: <:<[E, AA]): AA

  22. def getOrElse(a: A): A

    [use case]

    [use case]
    returns

    Full Signature

    def getOrElse[AA >: A](aa: ⇒ AA): AA

  23. def hashCode(): Int

    Definition Classes
    Any
  24. def invalid[B](fe: (NonEmptyVector[E]) ⇒ B)(fa: (A) ⇒ B): B

    Curried fold that starts with the invalid part.

    Curried fold that starts with the invalid part.

    B

    the resulting type

    fe

    the function to apply in the invalid case

    fa

    the function to apply in the valid case

    returns

    the result of one of the two functions

    See also

    fold

  25. def invalidMap[F](f: (E) ⇒ F): Result[F, A]

    Maps over the invalid value of this Result.

    Maps over the invalid value of this Result.

    This is the Functor over the invalid side.

    F

    the resulting invalid type

    f

    the function to apply if this Result is invalid

    returns

    the result of the function application in the Result context

  26. final def isInstanceOf[T0]: Boolean

    Definition Classes
    Any
  27. def isInvalid: Boolean

    returns

    true if this is an invalid Result

  28. def isValid: Boolean

    returns

    true if this is a valid Result

  29. def map[B](f: (A) ⇒ B): Result[E, B]

    Maps over the valid value of this Result.

    Maps over the valid value of this Result.

    This is the Functor over the valid side.

    B

    the resulting valid type

    f

    the function to apply if this Result is valid

    returns

    the result of the function application in the Result context

  30. def merge(other: Result[E, A]): Result[E, A]

    [use case]

    [use case]
    other

    returns

    Full Signature

    def merge[EE >: E, AA >: A](other: ⇒ Result[EE, AA]): Result[EE, NonEmptyVector[AA]]

  31. def nev: Result[E, NonEmptyVector[A]]

    returns

  32. def orElse(other: Result[E, A]): Result[E, A]

    [use case]

    [use case]
    other

    returns

    Full Signature

    def orElse[EE >: E, AA >: A](other: ⇒ Result[EE, AA]): Result[EE, AA]

  33. def productIterator: Iterator[Any]

    Definition Classes
    Product
  34. def productPrefix: String

    Definition Classes
    Product
  35. def recover(f: (E) ⇒ A): Result[E, A]

    [use case] Transforms an invalid Result into a valid one.

    [use case]

    Transforms an invalid Result into a valid one.

    f

    the function to transform an invalid value into a valid one

    returns

    a result that is always valid

    Full Signature

    def recover[AA >: A](f: (E) ⇒ AA): Result[E, AA]

  36. def recoverAll(f: (NonEmptyVector[E]) ⇒ A): Result[E, A]

    [use case] Transforms an invalid Result into a valid one.

    [use case]

    Transforms an invalid Result into a valid one.

    f

    the function to transform all invalid values into a valid one

    returns

    a result that is always valid

    Full Signature

    def recoverAll[AA >: A](f: (NonEmptyVector[E]) ⇒ AA): Result[E, AA]

  37. def recoverWith[F](f: (E) ⇒ Result[F, A]): Result[F, A]

    [use case] Continues validation with the provided function if this is an invalid Result.

    [use case]

    Continues validation with the provided function if this is an invalid Result.

    This is the Monadic Bind through the invalid value.

    F

    the resulting invalid type

    f

    the function to continue with

    returns

    the result of applying f over the invalid value in the Result context

    Full Signature

    def recoverWith[AA >: A, F](f: (E) ⇒ Result[F, AA]): Result[F, AA]

  38. def swap: Result[A, NonEmptyVector[E]]

    returns

  39. def swapped[EE, AA](f: (Result[A, NonEmptyVector[E]]) ⇒ Result[AA, NonEmptyVector[EE]]): Result[EE, NonEmptyVector[AA]]

    EE
    AA
    f
    returns

  40. def toEither: Either[NonEmptyVector[E], A]

    returns

  41. def toList: List[A]

    returns

  42. def toOption: Option[A]

    returns

    a scala.Option

  43. def toSeq: Seq[A]

    returns

  44. def toSet: Set[A]

    [use case]

    [use case]
    returns

    Full Signature

    def toSet[AA >: A]: Set[AA]

  45. def toStream: Stream[A]

    returns

  46. def toString(): String

    Definition Classes
    Any
  47. def toTry: Try[A]

    [use case]

    [use case]
    returns

    Full Signature

    def toTry(implicit ev: <:<[E, Throwable]): Try[A]

  48. def toVector: Vector[A]

    returns

  49. def valid[B](fa: (A) ⇒ B)(fe: (NonEmptyVector[E]) ⇒ B): B

    Curried fold that starts with the valid part.

    Curried fold that starts with the valid part.

    B

    the resulting type

    fa

    the function to apply in the valid case

    fe

    the function to apply in the invalid case

    returns

    the result of one of the two functions

    See also

    fold

  50. def valueOr(x: (E) ⇒ A): A

    [use case]

    [use case]
    x

    returns

    Full Signature

    def valueOr[AA >: A](x: (NonEmptyVector[E]) ⇒ AA): AA

  51. def void: Result[E, Unit]

    Discards the valid value if this is a valid Result.

    Discards the valid value if this is a valid Result.

    returns

    the result with the valid value set to Unit

    Since

    0.2.0

  52. def zip[B](b: Result[E, B]): Result[E, (A, B)]

    [use case] Combines two Result by zipping their valid values together, accumulating errors.

    [use case]

    Combines two Result by zipping their valid values together, accumulating errors.

    B

    b

    returns

    Full Signature

    def zip[EE >: E, B](b: Result[EE, B]): Result[EE, (A, B)]

Inherited from Serializable

Inherited from Serializable

Inherited from Product

Inherited from Equals

Inherited from Any

Accessors and Extractors

Methods to access or extract a value from this Result.

Transformers

Methods to transform the value of this Result.

Testers

Methods to test the value of this Result for some properties.

Translators

Methods to translate this Result into a different structure.

Combinators

Methods to combine multiple Results.

Ungrouped