scalaz.Validation.scala Maven / Gradle / Ivy
package scalaz
import scala.util.control.NonFatal
import scala.reflect.ClassTag
/**
* Represents either:
* - `Success(a)`, or
* - `Failure(e)`.
*
* Isomorphic to `scala.Either` and `scalaz.\/`. The motivation for a `Validation` is to provide the instance
* `Applicative[[a]Validation[E, a]]` that accumulate failures through a [[scalaz.Semigroup]]`[E]`.
*
* [[scalaz.NonEmptyList]] is commonly chosen as a type constructor for the type `E`. As a convenience,
* an alias `scalaz.ValidationNel[E]` is provided as a shorthand for `scalaz.Validation[NonEmptyList[E]]`,
* and a method `Validation#toValidationNel` converts `Validation[E]` to `ValidationNel[E]`.
*
* Example:
* {{{
* import scalaz._, std.AllInstances._
*
* def parseInt(s: String): Validation[String, Int] =
* try { Success(s.toInt) } catch { case ex: NumberFormatException => Failure(ex.getMessage) }
* val V = Applicative[ValidationNel[String, ?]]
*
* val x: ValidationNel[String, Int] =
* V.apply2(parseInt("1.x").toValidationNel, parseInt("1..0").toValidationNel)(_ * _)
* // Failure(NonEmptyList(For input string: "1..0", For input string: "1.x"))
* }}}
*
* @tparam E The type of the `Failure`
* @tparam A The type of the `Success`
*/
sealed abstract class Validation[+E, +A] extends Product with Serializable {
final class SwitchingValidation[X](s: => X){
def < X): X =
Validation.this match {
case Failure(_) => fail
case Success(_) => s
}
}
/** If this validation is success, return the given X value, otherwise, return the X value given to the return value. */
def :?>>[X](success: => X): SwitchingValidation[X] =
new SwitchingValidation[X](success)
/** Return `true` if this validation is success. */
def isSuccess: Boolean = this match {
case Success(_) => true
case Failure(_) => false
}
/** Return `true` if this validation is failure. */
def isFailure: Boolean = !isSuccess
/** Catamorphism. Run the first given function if failure, otherwise, the second given function. */
def fold[X](fail: E => X, succ: A => X): X = this match {
case Success(x) => succ(x)
case Failure(x) => fail(x)
}
/** Spin in tail-position on the success value of this validation. */
def loopSuccess[EE >: E, AA >: A, X](success: AA => X \/ Validation[EE, AA], failure: EE => X): X =
Validation.loopSuccess(this, success, failure)
/** Spin in tail-position on the failure value of this validation. */
def loopFailure[EE >: E, AA >: A, X](success: AA => X, failure: EE => X \/ Validation[EE, AA]): X =
Validation.loopFailure(this, success, failure)
/** Flip the failure/success values in this validation. Alias for `swap` */
def unary_~ : Validation[A, E] =
swap
/** Flip the failure/success values in this validation. Alias for `unary_~` */
def swap: Validation[A, E] =
this match {
case Failure(a) => Success(a)
case Success(b) => Failure(b)
}
/** Run the given function on this swapped value. Alias for `~` */
def swapped[EE, AA](k: Validation[A, E] => Validation[AA, EE]): Validation[EE, AA] =
k(swap).swap
/** Run the given function on this swapped value. Alias for `swapped` */
def ~[EE, AA](k: Validation[A, E] => Validation[AA, EE]): Validation[EE, AA] =
swapped(k)
/** Binary functor map on this validation. */
def bimap[C, D](f: E => C, g: A => D): Validation[C, D] =
this match {
case Failure(a) => Failure(f(a))
case Success(b) => Success(g(b))
}
/** Run the given function on the left value. */
def leftMap[C](f: E => C): Validation[C, A] =
this match {
case a @ Success(_) => a
case Failure(e) => Failure(f(e))
}
/** Binary functor traverse on this validation. */
def bitraverse[G[_] : Functor, C, D](f: E => G[C], g: A => G[D]): G[Validation[C, D]] = this match {
case Failure(a) => Functor[G].map(f(a))(Validation.failure)
case Success(b) => Functor[G].map(g(b))(Validation.success)
}
/** Map on the success of this validation. */
def map[B](f: A => B): Validation[E, B] = this match {
case Success(a) => Success(f(a))
case e @ Failure(_) => e
}
/** Traverse on the success of this validation. */
def traverse[G[_] : Applicative, EE >: E, B](f: A => G[B]): G[Validation[EE, B]] = this match {
case Success(a) => Applicative[G].map(f(a))(Validation.success)
case e @ Failure(_) => Applicative[G].point(e)
}
/** Run the side-effect on the success of this validation. */
def foreach[U](f: A => U): Unit = this match {
case Success(a) => f(a)
case Failure(_) =>
}
/** Apply a function in the environment of the success of this validation, accumulating errors. */
def ap[EE >: E, B](x: => Validation[EE, A => B])(implicit E: Semigroup[EE]): Validation[EE, B] = (this, x) match {
case (Success(a), Success(f)) => Success(f(a))
case (e @ Failure(_), Success(_)) => e
case (Success(_), e @ Failure(_)) => e
case (Failure(e1), Failure(e2)) => Failure(E.append(e2, e1))
}
/** Fold on the success of this validation. */
def foldRight[B](z: => B)(f: (A, => B) => B): B = this match {
case Success(a) => f(a, z)
case Failure(_) => z
}
/** Filter on the success of this validation. */
def filter[EE >: E](p: A => Boolean)(implicit M: Monoid[EE]): Validation[EE, A] =
this match {
case Failure(_) => this
case Success(e) => if(p(e)) this else Failure(M.zero)
}
/** Return `true` if this validation is a success value satisfying the given predicate. */
def exists(f: A => Boolean): Boolean = this match {
case Success(a) => f(a)
case Failure(_) => false
}
/** Return `true` if this validation is a failure value or the success value satisfies the given predicate. */
def forall(f: A => Boolean): Boolean = this match {
case Success(a) => f(a)
case Failure(_) => true
}
/** Return an empty list or list with one element on the success of this validation. */
def toList: List[A] =
this match {
case Failure(_) => Nil
case Success(a) => a :: Nil
}
/** Return an empty stream or stream with one element on the success of this validation. */
def toStream: Stream[A] =
this match {
case Failure(_) => Stream()
case Success(a) => Stream(a)
}
/** Return an empty option or option with one element on the success of this validation. Useful to sweep errors under the carpet. */
def toOption: Option[A] =
this match {
case Failure(_) => None
case Success(a) => Some(a)
}
/** Return an empty maybe or maybe with the element on the success of this validation. Useful to sweep errors under the carpet. */
def toMaybe[AA >: A]: Maybe[AA] =
this match {
case Failure(_) => Maybe.empty
case Success(a) => Maybe.just(a)
}
/** Convert to a core `scala.Either` at your own peril. */
def toEither: Either[E, A] =
this match {
case Success(a) => Right(a)
case Failure(e) => Left(e)
}
/** Return the success value of this validation or the given default if failure. Alias for `|` */
def getOrElse[AA >: A](x: => AA): AA =
this match {
case Failure(_) => x
case Success(a) => a
}
/** Return the success value of this validation or the given default if failure. Alias for `getOrElse` */
def |[AA >: A](x: => AA): AA =
getOrElse(x)
/** Return the success value of this validation or run the given function on the failure. */
def valueOr[AA >: A](x: E => AA): AA =
this match {
case Failure(a) => x(a)
case Success(b) => b
}
/** Return this if it is a success, otherwise, return the given value. Alias for `|||` */
def orElse[EE >: E, AA >: A](x: => Validation[EE, AA]): Validation[EE, AA] =
this match {
case Failure(_) => x
case Success(_) => this
}
/** Return this if it is a success, otherwise, return the given value. Alias for `orElse` */
def |||[EE >: E, AA >: A](x: => Validation[EE, AA]): Validation[EE, AA] =
orElse(x)
/**
* Sums up values inside validation, if both are success or failure. Returns first failure otherwise.
* {{{
* success(v1) +++ success(v2) → success(v1 + v2)
* success(v1) +++ failure(v2) → failure(v2)
* failure(v1) +++ success(v2) → failure(v1)
* failure(v1) +++ failure(v2) → failure(v1 + v2)
* }}}
*/
def +++[EE >: E, AA >: A](x: => Validation[EE, AA])(implicit M1: Semigroup[AA], M2: Semigroup[EE]): Validation[EE, AA] =
this match {
case Failure(a1) => x match {
case Failure(a2) => Failure(M2.append(a1, a2))
case Success(b2) => this
}
case Success(b1) => x match {
case b2 @ Failure(_) => b2
case Success(b2) => Success(M1.append(b1, b2))
}
}
/** Ensures that the success value of this validation satisfies the given predicate, or fails with the given value. */
def ensure[EE >: E](onFailure: => EE)(f: A => Boolean): Validation[EE, A] =
excepting({ case a if !f(a) => onFailure})
/** Compare two validations values for equality. */
def ===[EE >: E, AA >: A](x: Validation[EE, AA])(implicit EE: Equal[EE], EA: Equal[AA]): Boolean =
this match {
case Failure(e1) => x match {
case Failure(e2) => Equal[EE].equal(e1, e2)
case Success(_) => false
}
case Success(a1) => x match {
case Success(a2) => Equal[AA].equal(a1, a2)
case Failure(_) => false
}
}
/** Compare two validations values for ordering. */
def compare[EE >: E, AA >: A](x: Validation[EE, AA])(implicit EE: Order[EE], EA: Order[AA]): Ordering =
this match {
case Failure(e1) => x match {
case Failure(e2) => Order[EE].apply(e1, e2)
case Success(_) => Ordering.LT
}
case Success(a1) => x match {
case Success(a2) => Order[AA].apply(a1, a2)
case Failure(_) => Ordering.GT
}
}
/** Show for a validation value. */
def show[EE >: E, AA >: A](implicit SE: Show[EE], SA: Show[AA]): Cord =
this match {
case Failure(e) => ("Failure(": Cord) ++ Show[EE].show(e) :- ')'
case Success(a) => ("Success(": Cord) ++ Show[AA].show(a) :- ')'
}
/** If `this` and `that` are both success, or both a failure, combine them with the provided `Semigroup` for each. Otherwise, return the success. Alias for `+|+` */
def append[EE >: E, AA >: A](that: Validation[EE, AA])(implicit es: Semigroup[EE], as: Semigroup[AA]): Validation[EE, AA] = (this, that) match {
case (Success(a1), Success(a2)) => Success(as.append(a1, a2))
case (Success(_), Failure(_)) => this
case (Failure(_), Success(_)) => that
case (Failure(e1), Failure(e2)) => Failure(es.append(e1, e2))
}
/** If `this` and `that` are both success, or both a failure, combine them with the provided `Semigroup` for each. Otherwise, return the success. Alias for `append` */
def +|+[EE >: E, AA >: A](x: Validation[EE, AA])(implicit es: Semigroup[EE], as: Semigroup[AA]): Validation[EE, AA] = append(x)
/** If `this` is a success, return it; otherwise, if `that` is a success, return it; otherwise, combine the failures with the specified semigroup. */
def findSuccess[EE >: E, AA >: A](that: => Validation[EE, AA])(implicit es: Semigroup[EE]): Validation[EE, AA] = this match {
case Failure(e) => that match {
case Failure(e0) => Failure(es.append(e, e0))
case success => success
}
case success => success
}
/** Wraps the failure value in a [[scalaz.NonEmptyList]] */
def toValidationNel[EE >: E, AA >: A]: ValidationNel[EE, AA] =
this match {
case a @ Success(_) => a
case Failure(e) => Failure(NonEmptyList(e))
}
/** Convert to a disjunction. */
def disjunction: (E \/ A) =
this match {
case Success(a) => \/-(a)
case Failure(e) => -\/(e)
}
/** Run a disjunction function and back to validation again. Alias for `@\/` */
def disjunctioned[EE, AA](k: (E \/ A) => (EE \/ AA)): Validation[EE, AA] =
k(disjunction).validation
/** Run a disjunction function and back to validation again. Alias for `disjunctioned` */
def @\/[EE, AA](k: (E \/ A) => (EE \/ AA)): Validation[EE, AA] =
disjunctioned(k)
/**
* Return a Validation formed by the application of a partial function across the
* success of this value:
* {{{
* strings map (_.parseInt excepting { case i if i < 0 => new Exception(s"Int must be positive: $i") })
* }}}
* @since 7.0.2
*/
def excepting[EE >: E](pf: PartialFunction[A, EE]): Validation[EE, A] = {
import syntax.std.option._
this match {
case Success(s) => pf.lift(s) toFailure s
case _ => this
}
}
}
final case class Success[A](a: A) extends Validation[Nothing, A]
final case class Failure[E](e: E) extends Validation[E, Nothing]
object Validation extends ValidationInstances {
/** Spin in tail-position on the success value of the given validation. */
@annotation.tailrec
final def loopSuccess[E, A, X](d: Validation[E, A], success: A => X \/ Validation[E, A], failure: E => X): X =
d match {
case Failure(e) => failure(e)
case Success(a) => success(a) match {
case -\/(x) => x
case \/-(q) => loopSuccess(q, success, failure)
}
}
/** Spin in tail-position on the failure value of the given validation. */
@annotation.tailrec
final def loopFailure[E, A, X](d: Validation[E, A], success: A => X, failure: E => X \/ Validation[E, A]): X =
d match {
case Failure(e) => failure(e) match {
case -\/(x) => x
case \/-(q) => loopFailure(q, success, failure)
}
case Success(a) => success(a)
}
/** Import this if you wish to use `flatMap` */
object FlatMap {
@inline implicit def ValidationFlatMapRequested[E, A](d: Validation[E, A]): ValidationFlatMap[E, A] =
new ValidationFlatMap(d)
}
/** Construct a success validation value. */
def success[E, A]: A => Validation[E, A] =
Success(_)
/** Construct a failure validation value. */
def failure[E, A]: E => Validation[E, A] =
Failure(_)
/** Wrap a value in a `NonEmptyList` and construct a failure validation out of it. */
def failureNel[E, A](e: E): ValidationNel[E, A] =
Failure(NonEmptyList(e))
def fromTryCatchThrowable[T, E <: Throwable](a: => T)(implicit nn: NotNothing[E], ex: ClassTag[E]): Validation[E, T] = try {
Success(a)
} catch {
case e if ex.runtimeClass.isInstance(e) => Failure(e.asInstanceOf[E])
}
def fromTryCatchNonFatal[T](a: => T): Validation[Throwable, T] = try {
Success(a)
} catch {
case NonFatal(t) => Failure(t)
}
/** Construct a `Validation` from an `Either`. */
def fromEither[E, A](e: Either[E, A]): Validation[E, A] =
e.fold(failure, success)
}
sealed abstract class ValidationInstances extends ValidationInstances0 {
type \?/[+E, +A] =
Validation[E, A]
}
sealed abstract class ValidationInstances0 extends ValidationInstances1 {
implicit def ValidationOrder[E: Order, A: Order]: Order[Validation[E, A]] = new Order[Validation[E, A]] {
def order(f1: Validation[E, A], f2: Validation[E, A]) =
f1 compare f2
override def equal(f1: Validation[E, A], f2: Validation[E, A]) =
f1 === f2
}
implicit def ValidationMonoid[E: Semigroup, A: Monoid]: Monoid[Validation[E, A]] =
new Monoid[Validation[E, A]] {
def append(a1: Validation[E, A], a2: => Validation[E, A]) =
a1 +++ a2
def zero =
Success(Monoid[A].zero)
}
implicit def ValidationAssociative: Associative[Validation] = new Associative[Validation] {
override def reassociateLeft[A, B, C](f: Validation[A, Validation[B, C]]): Validation[Validation[A, B], C] =
f.fold(
a => Failure(Failure(a)),
_.fold(
b => Failure(Success(b)),
Success(_)
)
)
override def reassociateRight[A, B, C](f: Validation[Validation[A, B], C]): Validation[A, Validation[B, C]] =
f.fold(
_.fold(
Failure(_),
b => Success(Failure(b))
),
c => Success(Success(c))
)
}
}
final class ValidationFlatMap[E, A] private[scalaz](val self: Validation[E, A]) extends AnyVal {
/** Bind through the success of this validation. */
def flatMap[EE >: E, B](f: A => Validation[EE, B]): Validation[EE, B] =
self match {
case Success(a) => f(a)
case e @ Failure(_) => e
}
}
sealed abstract class ValidationInstances1 extends ValidationInstances2 {
implicit def ValidationEqual[E: Equal, A: Equal]: Equal[Validation[E, A]] =
new Equal[Validation[E, A]] {
def equal(a1: Validation[E, A], a2: Validation[E, A]) =
a1 === a2
}
implicit def ValidationShow[E: Show, A: Show]: Show[Validation[E, A]] =
Show.show(_.show)
implicit def ValidationSemigroup[E: Semigroup, A: Semigroup]: Semigroup[Validation[E, A]] =
new Semigroup[Validation[E, A]] {
def append(a1: Validation[E, A], a2: => Validation[E, A]) =
a1 +++ a2
}
}
sealed abstract class ValidationInstances2 extends ValidationInstances3 {
implicit def ValidationInstances1[L]: Traverse[Validation[L, ?]] with Cozip[Validation[L, ?]] with Plus[Validation[L, ?]] with Optional[Validation[L, ?]] =
new Traverse[Validation[L, ?]] with Cozip[Validation[L, ?]] with Plus[Validation[L, ?]] with Optional[Validation[L, ?]] {
override def map[A, B](fa: Validation[L, A])(f: A => B) =
fa map f
def traverseImpl[G[_] : Applicative, A, B](fa: Validation[L, A])(f: A => G[B]) =
fa.traverse(f)
override def foldRight[A, B](fa: Validation[L, A], z: => B)(f: (A, => B) => B) =
fa.foldRight(z)(f)
def cozip[A, B](x: Validation[L, A \/ B]) =
x match {
case l @ Failure(_) => -\/(l)
case Success(e) => e match {
case -\/(a) => -\/(Success(a))
case \/-(b) => \/-(Success(b))
}
}
def plus[A](a: Validation[L, A], b: => Validation[L, A]) =
a orElse b
def pextract[B, A](fa: Validation[L,A]): Validation[L,B] \/ A =
fa.fold(l => -\/(Failure(l)), \/.right)
}
}
sealed abstract class ValidationInstances3 {
implicit val ValidationInstances0 : Bitraverse[Validation] =
new Bitraverse[Validation] {
override def bimap[A, B, C, D](fab: Validation[A, B])
(f: A => C, g: B => D) = fab bimap (f, g)
def bitraverseImpl[G[_] : Applicative, A, B, C, D](fab: Validation[A, B])
(f: A => G[C], g: B => G[D]) =
fab.bitraverse(f, g)
}
implicit def ValidationApplicative[L: Semigroup]: Applicative[Validation[L, ?]] =
new Applicative[Validation[L, ?]] {
override def map[A, B](fa: Validation[L, A])(f: A => B) =
fa map f
def point[A](a: => A) =
Success(a)
def ap[A, B](fa: => Validation[L, A])(f: => Validation[L, A => B]) =
fa ap f
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy