Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Processing a list of Scalaz6 Validation

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?

like image 310
Edmondo1984 Avatar asked Jan 09 '13 10:01

Edmondo1984


1 Answers

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.

like image 138
Travis Brown Avatar answered Nov 12 '22 23:11

Travis Brown