21

I need to flatten a sequence of cats.data.ValidatedNel[E, T] values to a single ValidatedNel value:

val results: Seq[cats.data.ValidatedNel[E, T]] = ???

val flattenedResult: cats.data.ValidatedNel[E, T]

I can do it like this:

import cats.std.list._, cats.syntax.cartesian._

results.reduce(_ |@| _ map { case _ => validatedValue })

but wonder if a pre-defined library methods exists.

Tvaroh
  • 6,645
  • 4
  • 51
  • 55

1 Answers1

29

It depends on how you want to combine them (what is validatedValue in your question ?)

import cats.data.{Validated, ValidatedNel}
import cats.implicits._

val validations1 = List(1.validNel[String], 2.valid, 3.valid)
val validations2 = List(1.validNel[String], "kaboom".invalidNel, "boom".invalidNel)

If you want to combine the Ts, you can use Foldable.combineAll which uses a Monoid[T] :

val valSum1 = validations1.combineAll 
// Valid(6)
val valSum2 = validations2.combineAll 
// Invalid(OneAnd(kaboom,List(boom)))

If you want to get a ValidationNel[String, List[T]], you can use Traverse.sequence :

val valList1: ValidatedNel[String, List[Int]] = validations1.sequence
// Valid(List(1, 2, 3))
val valList2: ValidatedNel[String, List[Int]] = validations2.sequence
// Invalid(OneAnd(kaboom,List(boom)))

If you don't care about the result, which seems to be the case, you can use Foldable.sequence_.

val result1: ValidatedNel[String, Unit] = validations1.sequence_
//  Valid(())
val result2: ValidatedNel[String, Unit] = validations2.sequence_
// Invalid(OneAnd(kaboom,List(boom)))

validations1.sequence_.as(validatedValue) // as(x) is equal to map(_ => x)
Tvaroh
  • 6,645
  • 4
  • 51
  • 55
Peter Neyens
  • 9,770
  • 27
  • 33
  • I'm sorry, `s` in the original question (now `validatedValue`) is a value that's being validated, i.e. `T`. – Tvaroh Jun 12 '16 at 12:33
  • 2
    Looks like `combineAll` is what I need. I needed to add `.toList` call on my `Seq`, btw. – Tvaroh Jun 12 '16 at 12:35
  • @Tvaroh Yes, cats has no type class instances for `Seq` so you need to go to `List` or `Vector`. – Peter Neyens Jun 12 '16 at 12:44
  • Depending on how you got the `List[ValidatedNel[String, T]]`, it is possible you could replace the functions above by `foldMap(f)`, `traverseU(f)` and `traverseU_(f)`. – Peter Neyens Jun 12 '16 at 12:49
  • If you use `as` you discard the result (the `List[T]` inside `Validated`) so you can use `sequence_` / `sequenceU_` (which returns `Unit` inside `Validated`). Because `Validated` is a type `F[_, _]` scala cannot figure out the types by itself if you use `sequence_`, so you can use `sequenceU_` which uses some trickery to infer the types for you. This will however be fixed in 2.12 (see [this PR](https://github.com/scala/scala/pull/5102)). In the meantime you could use [this compiler plugin](https://github.com/milessabin/si2712fix-plugin), in which case you can use `sequence_`. – Peter Neyens Jun 13 '16 at 11:34
  • sequenceU and traverseU was removed in cats 1.0. -Ypartial-unification and sequence as commented above should still work though – RikardA Apr 08 '18 at 18:08