To answer your question, this example uses kind projector compiler plugin for partial application of type parameters. Depending on how familiar you are with Scala terminology, that may not make complete sense. Basically it means that some Scala methods expect arguments of a certain shape, and you have to help the compiler with that by adding a question mark in the appropriate place.
Also be sure to add some imports.
import cats._
import cats.data._
import cats.implicits._
import Validated.{ valid, invalid }
Then given,
val a = valid[String,Int](1)
val as = List(a)
you can use List.sequence
to convert a List[Validated[NonEmptyList[E, T]]]
to a Validated[E, List[T]]
.
as.sequence[Validated[String,?], Int]
In this case if you use a single String as the error type, the Strings that represent the error type will be concatenated together into a single string. This is where the monoid constraint comes from that you mentioned
- it helps combine the strings with string concatenation. Other error types may work as well, such as List[String]
or List[ErrorType]
for the error type instead of a simple String
. In the case where you want to accumulate errors, use a List of some error type,
val a = valid[List[String],Int](1)
val b = invalid[List[String], Int]("some".lift[List])
List(a,b).sequence[Validated[List[String],?], Int]
results in
res29: Validated[List[String], List[Int]] = Invalid(List("some"))
The lift operation is needed above to go from a String to a List of String.