8

Is there an idiomatic way to handle a collection of Validation in Scalaz6?

val results:Seq[Validation[A,B]]
val exceptions = results.collect{case Failure(exception)=>exception}
exceptions.foreach{logger.error("Error when starting up ccxy gottware",_)}
val success = results.collect{case Success(data)=>data}
success.foreach {data => data.push}
if (exceptions.isEmpty)
   containers.foreach( _.start())

I could think of using a fold when looping on results, but what about the final test?

Edmondo
  • 19,559
  • 13
  • 62
  • 115

1 Answers1

9

The usual way to work with a list of validations is to use sequence to turn the list into a Validation[A, List[B]], which will be be empty (i.e., a Failure) if there were any errors along the way.

Sequencing a Validation accumulates errors (as opposed to Either, which fails immediately) in the semigroup of the left-hand type. This is why you often see ValidationNEL (where the NEL stands for NonEmptyList) used instead of simply Validation. So for example if you have this result type:

import scalaz._, Scalaz._

type ExceptionsOr[A] = ValidationNEL[Exception, A]

And some results:

val results: Seq[ExceptionsOr[Int]] = Seq(
  "13".parseInt.liftFailNel, "42".parseInt.liftFailNel
)

Sequencing will give you the following:

scala> results.sequence
res0: ExceptionsOr[Seq[Int]] = Success(List(13, 42))

If we had some errors like this, on the other hand:

val results: Seq[ExceptionsOr[Int]] = Seq(
  "13".parseInt.liftFailNel, "a".parseInt.liftFailNel, "b".parseInt.liftFailNel
)

We'd end up with a Failure (note that I've reformatted the output to make it legible here):

scala> results.sequence
res1: ExceptionsOr[Seq[Int]] = Failure(
  NonEmptyList(
    java.lang.NumberFormatException: For input string: "a",
    java.lang.NumberFormatException: For input string: "b"
  )
)

So in your case you'd write something like this:

val results: Seq[ValidationNEL[A, B]]

results.sequence match {
  case Success(xs) => xs.foreach(_.push); containers.foreach(_.start())
  case Failure(exceptions) => exceptions.foreach(
    logger.error("Error when starting up ccxy gottware", _)
  )
}

See my answers here and here for more detail about sequence and about Validation more generally.

Community
  • 1
  • 1
Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • What about the complimentary case? I wanna succeed if at least a Validation has succeeded ? – Edmondo Feb 18 '13 at 10:48
  • That sounds like you may be working against the grain of `Validation`. In 7 you could reduce the list with `|||`, but in 6 you'd need something like the sum using the semigroup for the right projection of `Either` (or you could write your own `|||`). – Travis Brown Feb 18 '13 at 13:21
  • Can you point me towards some resources who explain what a Semigroup is? – Edmondo Feb 18 '13 at 14:20