Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reduced usage of fold in Validated #1976

Merged
merged 2 commits into from
Oct 17, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 114 additions & 48 deletions core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,55 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
case Valid(a) => fa(a)
}

def isValid: Boolean = fold(_ => false, _ => true)
def isInvalid: Boolean = fold(_ => true, _ => false)
def isValid: Boolean = this match {
case Invalid(_) => false
case _ => true
}

def isInvalid: Boolean = this match {
case Invalid(_) => true
case _ => false
}

/**
* Run the side-effecting function on the value if it is Valid
*/
def foreach(f: A => Unit): Unit = fold(_ => (), f)
def foreach(f: A => Unit): Unit = this match {
case Valid(a) => f(a)
case _ => ()
}

/**
* Return the Valid value, or the default if Invalid
*/
def getOrElse[B >: A](default: => B): B = fold(_ => default, identity)
def getOrElse[B >: A](default: => B): B = this match {
case Valid(a) => a
case _ => default
}

/**
* Return the Valid value, or the result of f if Invalid
*/
def valueOr[B >: A](f: E => B): B = fold(f, identity)
def valueOr[B >: A](f: E => B): B = this match {
case Invalid(e) => f(e)
case Valid(a) => a
}

/**
* Is this Valid and matching the given predicate
*/
def exists(predicate: A => Boolean): Boolean = fold(_ => false, predicate)
def exists(predicate: A => Boolean): Boolean = this match {
case Valid(a) => predicate(a)
case _ => false
}

/**
* Is this Invalid or matching the predicate
*/
def forall(f: A => Boolean): Boolean = fold(_ => true, f)
def forall(f: A => Boolean): Boolean = this match {
case Valid(a) => f(a)
case _ => true
}

/**
* Return this if it is Valid, or else fall back to the given default.
Expand All @@ -50,7 +72,7 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
def orElse[EE, AA >: A](default: => Validated[EE, AA]): Validated[EE, AA] =
this match {
case v @ Valid(_) => v
case Invalid(_) => default
case _ => default
}

/**
Expand All @@ -68,29 +90,41 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
/**
* Converts the value to an Either[E, A]
*/
def toEither: Either[E, A] = fold(Left.apply, Right.apply)
def toEither: Either[E, A] = this match {
case Invalid(e) => Left(e)
case Valid(a) => Right(a)
}

/**
* Returns Valid values wrapped in Some, and None for Invalid values
*/
def toOption: Option[A] = fold(_ => None, Some.apply)
def toOption: Option[A] = this match {
case Valid(a) => Some(a)
case _ => None
}

/**
* Returns Valid values wrapped in Ior.Right, and None for Ior.Left values
*/
def toIor: Ior[E, A] = fold(Ior.left, Ior.right)
def toIor: Ior[E, A] = this match {
case Invalid(e) => Ior.Left(e)
case Valid(a) => Ior.Right(a)
}

/**
* Convert this value to a single element List if it is Valid,
* otherwise return an empty List
*/
def toList: List[A] = fold(_ => Nil, List(_))
def toList: List[A] = this match {
case Valid(a) => List(a)
case _ => Nil
}

/** Lift the Invalid value into a NonEmptyList. */
def toValidatedNel[EE >: E, AA >: A]: ValidatedNel[EE, AA] =
this match {
case v @ Valid(_) => v
case Invalid(e) => Validated.invalidNel(e)
case Invalid(e) => Validated.invalidNel(e)
}

/**
Expand All @@ -108,20 +142,25 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
fold(fe andThen Invalid.apply,
fa andThen Valid.apply)

def compare[EE >: E, AA >: A](that: Validated[EE, AA])(implicit EE: Order[EE], AA: Order[AA]): Int = fold(
a => that.fold(EE.compare(a, _), _ => -1),
b => that.fold(_ => 1, AA.compare(b, _))
)
def compare[EE >: E, AA >: A](that: Validated[EE, AA])(implicit EE: Order[EE], AA: Order[AA]): Int = (this, that) match {
case (Valid(a), Valid(aa)) => AA.compare(a, aa)
case (Invalid(e), Invalid(ee)) => EE.compare(e, ee)
case (Invalid(_), _) => -1
case (Valid(_), _) => 1
}

def partialCompare[EE >: E, AA >: A](that: Validated[EE, AA])(implicit EE: PartialOrder[EE], AA: PartialOrder[AA]): Double = fold(
a => that.fold(EE.partialCompare(a, _), _ => -1),
b => that.fold(_ => 1, AA.partialCompare(b, _))
)
def partialCompare[EE >: E, AA >: A](that: Validated[EE, AA])(implicit EE: PartialOrder[EE], AA: PartialOrder[AA]): Double = (this, that) match {
case (Valid(a), Valid(aa)) => AA.partialCompare(a, aa)
case (Invalid(e), Invalid(ee)) => EE.partialCompare(e, ee)
case (Invalid(_), _) => -1
case (Valid(_), _) => 1
}

def ===[EE >: E, AA >: A](that: Validated[EE, AA])(implicit EE: Eq[EE], AA: Eq[AA]): Boolean = fold(
a => that.fold(EE.eqv(a, _), _ => false),
b => that.fold(_ => false, AA.eqv(b, _))
)
def ===[EE >: E, AA >: A](that: Validated[EE, AA])(implicit EE: Eq[EE], AA: Eq[AA]): Boolean = (this, that) match {
case (Invalid(e), Invalid(ee)) => EE.eqv(e, ee)
case (Valid(a), Valid(aa)) => AA.eqv(a, aa)
case _ => false
}

/**
* From Apply:
Expand Down Expand Up @@ -149,40 +188,54 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
/**
* Apply a function to a Valid value, returning a new Valid value
*/
def map[B](f: A => B): Validated[E, B] = bimap(identity, f)
def map[B](f: A => B): Validated[E, B] = this match {
case i @ Invalid(_) => i
case Valid(a) => Valid(f(a))
}

/**
* Apply a function to an Invalid value, returning a new Invalid value.
* Or, if the original valid was Valid, return it.
*/
def leftMap[EE](f: E => EE): Validated[EE, A] = bimap(f, identity)
def leftMap[EE](f: E => EE): Validated[EE, A] = this match {
case a @ Valid(_) => a
case Invalid(e) => Invalid(f(e))
}

/**
* When Valid, apply the function, marking the result as valid
* inside the Applicative's context,
* when Invalid, lift the Error into the Applicative's context
*/
def traverse[F[_], EE >: E, B](f: A => F[B])(implicit F: Applicative[F]): F[Validated[EE, B]] =
fold(e => F.pure(Invalid(e)),
a => F.map(f(a))(Valid.apply))
def traverse[F[_], EE >: E, B](f: A => F[B])(implicit F: Applicative[F]): F[Validated[EE, B]] = this match {
case Valid(a) => F.map(f(a))(Valid.apply)
case e @ Invalid(_) => F.pure(e)
}

/**
* apply the given function to the value with the given B when
* valid, otherwise return the given B
*/
def foldLeft[B](b: B)(f: (B, A) => B): B =
fold(_ => b, f(b, _))
def foldLeft[B](b: B)(f: (B, A) => B): B = this match {
case Valid(a) => f(b, a)
case _ => b
}

/**
* Lazily-apply the given function to the value with the given B
* when valid, otherwise return the given B.
*/
def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fold(_ => lb, a => f(a, lb))
def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = this match {
case Valid(a) => f(a, lb)
case _ => lb
}


def show[EE >: E, AA >: A](implicit EE: Show[EE], AA: Show[AA]): String = this match {
case Invalid(e) => s"Invalid(${EE.show(e)})"
case Valid(a) => s"Valid(${AA.show(a)})"
}

def show[EE >: E, AA >: A](implicit EE: Show[EE], AA: Show[AA]): String =
fold(e => s"Invalid(${EE.show(e)})",
a => s"Valid(${AA.show(a)})")

/**
* Apply a function (that returns a `Validated`) in the valid case.
Expand Down Expand Up @@ -222,7 +275,10 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
case Invalid(e) => Valid(e)
}

def merge[EE >: E](implicit ev: A <:< EE): EE = fold(identity, ev.apply)
def merge[EE >: E](implicit ev: A <:< EE): EE = this match {
case Invalid(e) => e
case Valid(a) => ev(a)
}

/**
* Ensure that a successful result passes the given predicate,
Expand All @@ -235,8 +291,10 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
* res0: Validated[IllegalArgumentException, String] = Invalid(java.lang.IllegalArgumentException: Must not be empty)
* }}}
*/
def ensure[EE >: E](onFailure: => EE)(f: A => Boolean): Validated[EE, A] =
fold(_ => this, a => if (f(a)) this else Validated.invalid(onFailure))
def ensure[EE >: E](onFailure: => EE)(f: A => Boolean): Validated[EE, A] = this match {
case Valid(a) => if (f(a)) this else Validated.invalid(onFailure)
case _ => this
}

/**
* Ensure that a successful result passes the given predicate,
Expand All @@ -249,8 +307,10 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
* res0: Validated[IllegalArgumentException, String] = Invalid(java.lang.IllegalArgumentException: Must be longer than 3, provided 'ab')
* }}}
*/
def ensureOr[EE >: E](onFailure: A => EE)(f: A => Boolean): Validated[EE, A] =
fold(_ => this, a => if (f(a)) this else Validated.invalid(onFailure(a)))
def ensureOr[EE >: E](onFailure: A => EE)(f: A => Boolean): Validated[EE, A] = this match {
case Valid(a) => if (f(a)) this else Validated.invalid(onFailure(a))
case _ => this
}
}

object Validated extends ValidatedInstances with ValidatedFunctions{
Expand Down Expand Up @@ -415,14 +475,18 @@ private[data] sealed abstract class ValidatedInstances2 {
override def reduceRightOption[A](fa: Validated[E, A])(f: (A, Eval[A]) => Eval[A]): Eval[Option[A]] =
Now(fa.toOption)

override def size[A](fa: Validated[E, A]): Long =
fa.fold(_ => 0L, _ => 1L)
override def size[A](fa: Validated[E, A]): Long = fa match {
case Invalid(_) => 0L
case _ => 1L
}

override def get[A](fa: Validated[E, A])(idx: Long): Option[A] =
if (idx == 0L) fa.toOption else None

override def foldMap[A, B](fa: Validated[E, A])(f: A => B)(implicit B: Monoid[B]): B =
fa.fold(_ => B.empty, f)
override def foldMap[A, B](fa: Validated[E, A])(f: A => B)(implicit B: Monoid[B]): B = fa match {
case Valid(a) => f(a)
case _ => B.empty
}

override def find[A](fa: Validated[E, A])(f: A => Boolean): Option[A] =
fa.toOption.filter(f)
Expand All @@ -433,8 +497,10 @@ private[data] sealed abstract class ValidatedInstances2 {
override def forall[A](fa: Validated[E, A])(p: A => Boolean): Boolean =
fa.forall(p)

override def toList[A](fa: Validated[E, A]): List[A] =
fa.fold(_ => Nil, _ :: Nil)
override def toList[A](fa: Validated[E, A]): List[A] = fa match {
case Valid(a) => a :: Nil
case _ => Nil
}

override def isEmpty[A](fa: Validated[E, A]): Boolean = fa.isInvalid
}
Expand Down