5

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)

fanf42
  • 1,828
  • 15
  • 22

4 Answers4

5

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.

mpilquist
  • 3,855
  • 21
  • 22
4

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))
George
  • 8,368
  • 12
  • 65
  • 106
  • Note that this uses the default monoid for Validation, which delegates to +++ for appending. +++ returns the first failure, not the appended failure. E.g.: scala> List(1.failure[String], 2.failure[String]).reduceLeft { _ |+| _ } res0: scalaz.Validation[Int,String] = Failure(1) – mpilquist Aug 10 '12 at 16:40
  • @mpilquist Well, this behavior has changed recently: https://github.com/scalaz/scalaz/pull/114. Updated my answer to be more precise about this. – George Aug 10 '12 at 16:46
  • Hmm take a look at Tony's disjunction merge. It appears that the change in pull 114 was lost. The code I ran above is from a local build of scalaz-seven that's up to date. – mpilquist Aug 10 '12 at 16:54
  • Now that's confusing. I was so sure, because I was actually changing that instance. Updated my answer. – George Aug 10 '12 at 17:00
  • Yeah, I bet it was accidental. I'd mention it to Tony and see what he thinks. – mpilquist Aug 10 '12 at 17:16
  • If the default goes back to the change from pull 114, then l.concatenate would work too. – mpilquist Aug 10 '12 at 17:18
  • I've created another pull request to restore it: https://github.com/scalaz/scalaz/pull/129. – George Aug 10 '12 at 17:54
  • See also [_Validation_](http://eed3si9n.com/learning-scalaz/Validation.html) from [_Learning Scalaz_](http://eed3si9n.com/learning-scalaz), and [a similar answer to a related question](http://stackoverflow.com/a/12309023/700420) for more help and usage with `NonEmptyList`. – Michael Ahlers Aug 08 '16 at 21:03
1

What you need is approximately switching an Either[E, A] into a Writer[List[E], A]. The Writer monad logs the errors you encountered.

ron
  • 9,262
  • 4
  • 40
  • 73
0

Sounds like you want a pair (SomveValue, List[T]) where T is your 'Failure' although I'd call it 'Warning' or 'Log' since you still get a result, so its not really a failure.

Don't know if Scalaz has anything fancy for this though.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
  • (W, A) is the Writer[A] monad, W usually being a semigroup (able to accumulate). – ron Sep 08 '12 at 19:59