Skip to content

Commit

Permalink
Adds Monoid instance for Validated
Browse files Browse the repository at this point in the history
  • Loading branch information
mikejcurry committed Dec 5, 2015
1 parent 925db99 commit c6a457b
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 2 deletions.
22 changes: 21 additions & 1 deletion core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,21 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable {
case Valid(a) => f(a)
case i @ Invalid(_) => i
}

/**
* Combine this `Validated` with another `Validated`, using the `Semigroup`
* instances of the underlying `E` and `A` instances. The resultant `Validated`
* will be `Valid`, if, and only if, both this `Validated` instance and the
* supplied `Validated` instance are also `Valid`.
*/
def combine[EE >: E, AA >: A](that: Validated[EE, AA])(implicit EE: Semigroup[EE], AA: Semigroup[AA]): Validated[EE, AA] =
(this, that) match {
case (Valid(a), Valid(b)) => Valid(AA.combine(a, b))
case (Invalid(a), Invalid(b)) => Invalid(EE.combine(a, b))
case (Invalid(_), _) => this
case _ => that
}

}

object Validated extends ValidatedInstances with ValidatedFunctions{
Expand All @@ -177,6 +192,12 @@ object Validated extends ValidatedInstances with ValidatedFunctions{


private[data] sealed abstract class ValidatedInstances extends ValidatedInstances1 {

implicit def validatedMonoid[A, B](implicit A: Semigroup[A], B: Monoid[B]): Monoid[Validated[A, B]] = new Monoid[Validated[A, B]] {
def empty: Validated[A, B] = Valid(B.empty)
def combine(x: Validated[A, B], y: Validated[A, B]): Validated[A, B] = x combine y
}

implicit def validatedOrder[A: Order, B: Order]: Order[Validated[A,B]] = new Order[Validated[A,B]] {
def compare(x: Validated[A,B], y: Validated[A,B]): Int = x compare y
override def partialCompare(x: Validated[A,B], y: Validated[A,B]): Double = x partialCompare y
Expand Down Expand Up @@ -282,4 +303,3 @@ trait ValidatedFunctions {
*/
def fromOption[A, B](o: Option[B], ifNone: => A): Validated[A,B] = o.fold(invalid[A, B](ifNone))(valid)
}

11 changes: 10 additions & 1 deletion tests/src/test/scala/cats/tests/ValidatedTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeTests, Se
import org.scalacheck.{Gen, Arbitrary}
import org.scalacheck.Arbitrary._
import cats.laws.discipline.arbitrary._
import algebra.laws.OrderLaws
import algebra.laws.{OrderLaws, GroupLaws}

import scala.util.Try

Expand All @@ -22,6 +22,8 @@ class ValidatedTests extends CatsSuite {
checkAll("Validated[String, Int]", OrderLaws[Validated[String, Int]].order)
checkAll("Order[Validated[String, Int]]", SerializableTests.serializable(Order[Validated[String, Int]]))

checkAll("Monoid[Validated[String, Int]]", GroupLaws[Validated[String, Int]].monoid)

{
implicit val S = ListWrapper.partialOrder[String]
implicit val I = ListWrapper.partialOrder[Int]
Expand Down Expand Up @@ -143,4 +145,11 @@ class ValidatedTests extends CatsSuite {
Validated.fromOption(o, s).toOption should === (o)
}
}

test("isValid after combine, iff both are valid") {
forAll { (lhs: Validated[Int, String], rhs: Validated[Int, String]) =>
lhs.combine(rhs).isValid should === (lhs.isValid && rhs.isValid)
}
}

}

0 comments on commit c6a457b

Please sign in to comment.