From e66f7a1b7d71739ab63d9d7a5ff8349224b117a8 Mon Sep 17 00:00:00 2001 From: Michael Zuber Date: Wed, 2 Nov 2016 09:36:14 -0400 Subject: [PATCH 1/2] Added findValid method on Validated that accumulates errors. Closes #1447 --- core/src/main/scala/cats/data/Validated.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index d55915dab5..708f1156a0 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -44,6 +44,8 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { /** * Return this if it is Valid, or else fall back to the given default. + * The functionality is similar to that of [[findValid]] except for failure accumulation, + * where here only the error on the right is preserved and the error on the left is ignored. */ def orElse[EE, AA >: A](default: => Validated[EE, AA]): Validated[EE, AA] = this match { @@ -51,6 +53,18 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { case Invalid(_) => default } + /** + * If `this` is valid return `this`, otherwise if `that` is valid return `that`, otherwise combine the failures. + * This is similar to [[orElse]] except that here failures are accumulated. + */ + def findValid[EE >: E, AA >: A](that: => Validated[EE, AA])(implicit EE: Semigroup[EE]): Validated[EE, AA] = this match { + case v @ Valid(_) => v + case Invalid(e) => that match { + case v @ Valid(_) => v + case Invalid(ee) => Invalid(EE.combine(e, ee)) + } + } + /** * Converts the value to an Either[E, A] */ From 31021548a12dfa902bba456ee3c25e71b0c2aff0 Mon Sep 17 00:00:00 2001 From: Michael Zuber Date: Fri, 4 Nov 2016 10:52:25 -0400 Subject: [PATCH 2/2] Added tests for orElse and findValid on Validated --- .../scala/cats/tests/ValidatedTests.scala | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index d40ee2cf6e..7904f7a860 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -114,6 +114,25 @@ class ValidatedTests extends CatsSuite { } } + test("findValid accumulates failures") { + forAll { (v: Validated[String, Int], u: Validated[String, Int]) => + v findValid u shouldEqual { (v, u) match { + case (vv @ Valid(_), _) => vv + case (_, uu @ Valid(_)) => uu + case (Invalid(s1), Invalid(s2)) => Invalid(s1 ++ s2) + }} + } + } + + test("orElse ignores left failure") { + forAll { (v: Validated[String, Int], u: Validated[String, Int]) => + v orElse u shouldEqual { (v, u) match { + case (vv @ Valid(_), _) => vv + case (_, uu) => uu + }} + } + } + test("valueOr consistent with swap then map then merge") { forAll { (v: Validated[String, Int], f: String => Int) => v.valueOr(f) should === (v.swap.map(f).merge)