I started to use Scalaz 7 Validation and/or disjunction to process a list of possibly failing operation and managing their result.
There is two well documented case for that kind of use cases:
1/ You want to check a list of conditions on something, and accumulate each error if any. Here, you always go the end of list, and in case of any error, you have failure as global result. And that's an applicative functor at work.
2/ You want to execute several steps that may fail, and stop on the first one failing. Here, we have a monad that goes nicely in Scala for-comprehension.
So, I have two other use cases that are among the same lines, but don't seems to feet well on any precedent case: I want to process a list of step, possibly failing, and accumulate both error and success results (ex: it's a list of modification on files, errors may happen because that's the outer world, and success are patch that I want to keep for later).
The difference on the two use case is only if I want to stop early (on the first error) or go to the end of the list.
OK, so what is the correct thing for that ?
(writting the question leads me to think that it's just a simple foldLeft, does it ? I will let the question here to validate, and if anybody else wonder)
Take a look at Validation#append
or its alias Validation#+|+
. Given two validations, if both are success, it returns success of the values appended. If both are failures, it returns failure of the values appended. Otherwise, it returns the successful value. This requires an implicit Semigroup instance for the success type.
I'd do something like this:
scala> List(1.success[String], 2.success[String], "3".failure[Int], "4".failure[Int]).partition(_.isSuccess)
res2: (List[scalaz.Validation[java.lang.String,Int]], List[scalaz.Validation[java.lang.String,Int]]) = (List(Success(1), Success(2)),List(Failure(3), Failure(4)))
scala> val fun = (_:List[Validation[String, Int]]).reduceLeft(_ append _)
fun: List[scalaz.Validation[String,Int]] => scalaz.Validation[String,Int] = <function1>
scala> fun <-: res2 :-> fun
res3: (scalaz.Validation[String,Int], scalaz.Validation[String,Int]) = (Success(3),Failure(34))
UPD: With #129 and #130 merged, you can change fun
to (_:List[Validation[String, Int]]).concatenate
or (_:List[Validation[String, Int]]).suml
Or bimap
like this:
scala> List(1.success[String], 2.success[String], "3".failure[Int], "4".failure[Int]).partition(_.isSuccess).bimap(_.suml, _.suml)
res6: (scalaz.Validation[java.lang.String,Int], scalaz.Validation[java.lang.String,Int]) = (Success(3),Failure(34))
What you need is approximately switching an Either[E, A]
into a Writer[List[E], A]
. The Writer
monad logs the errors you encountered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With