Scalaz validation: convert sequence of validations to a single validation
Asked Answered
E

2

14

I am using scalaz validation, and have some code to validate products.

def validateProduct(product: Option[Product]): ValidationNel[String, Product] = ???

Given a list of products, I want to get a single validation containing the whole list as a successful value or a list of validation errors. It seems like some kind of fold should do it, but I am not sure what the combination function should be.

 def validateProducts(products: Seq[Option[Product]]): ValidationNel[String, Seq[Product]] = {
    val listOfValidations: Seq[ValidationNel[String, Product]] = products.map(validateProduct _)
    val validatedList:ValidationNel[Seq[String], Seq[Product]] = ??? // what to do here?
    ???
  }

Any help is appreciated

Experimental answered 16/10, 2014 at 0:0 Comment(0)
L
17

If instead of a ValidationNel[List[String], List[Product]] you want a ValidationNel[String, List[Product]] (i.e., all the failures in the same list), you can just use traverse:

val result: ValidationNel[String, List[Product]] =
  products.toList.traverseU(validateProduct)

Note that I've converted the Seq to a List as there are no type class instances for raw Seq, and I'm using traverseU rather than traverse as Scala's type inference doesn't quite work for non-trivial type constructors like ValidationNel

Lane answered 16/10, 2014 at 0:15 Comment(1)
Very clean solutionBrause
P
7

You can use fold with applicative

  import scalaz.syntax.validation._
  import scalaz.syntax.applicative._

  case class Product(name: String)

  val allGood = Seq(
    Product("a").successNel[String],
    Product("b").successNel[String]
  )

  val aggregated: ValidationNel[String, Seq[Product]] = 
    allGood.foldLeft(Seq.empty[Product].successNel[String]) {
    case (acc , v) => (acc |@| v)(_ :+ _)
  }

  println(aggregated)
Pustule answered 16/10, 2014 at 0:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.