Cats Validated with mapN

  • A+
Category:Languages

I'm a beginner with Cats. I have an error with Validated cats. I use a list accumulator like that :

case class Type(  name: String,  pattern: String,  primitiveType: PrimitiveType = PrimitiveType.string,  sample: Option[String] = None,  comment: Option[String] = None,  stat: Option[Stat] = None ) {  type ValidationResult[A] = Validated[List[String], A]   def checkValidity(): ValidationResult[Boolean] = {   val errorList: mutable.MutableList[String] = mutable.MutableList.empty    val patternIsValid = Try {    primitiveType match {     case PrimitiveType.struct =>     case PrimitiveType.date =>       new SimpleDateFormat(pattern)     case PrimitiveType.timestamp =>       pattern match {         case "epoch_second" | "epoch_milli" =>         case _ if PrimitiveType.formatters.keys.toList.contains(pattern) =>         case _ =>           DateTimeFormatter.ofPattern(pattern)       }     case _ =>       Pattern.compile(pattern)    }  }  if (patternIsValid.isFailure)   errorList += s"Invalid Pattern $pattern in type $name"  val ok = sample.forall(this.matches)  if (!ok)   errorList += s"Sample $sample does not match pattern $pattern in type $name"  if (errorList.nonEmpty)   Invalid(errorList.toList)  else   Valid(true) } } 

When I use this function with my case class Types :

case class Types(types: List[Type]) {   type ValidationResult[A] = Validated[List[String], A]   def checkValidity(): ValidationResult[Boolean] = {    val typeNames = types.map(_.name)    val dup: ValidationResult[Boolean] =    duplicates(typeNames, s"%s is defined %d times. A type can only be defined once.")   (dup,types.map(_.checkValidity()).sequence).mapN((_,_) => true)  } } 

I have this error

Error:(29, 39) Cannot prove that cats.data.Validated[List[String],Boolean] <:< G[A]. (dup,types.map(_.checkValidity()).sequence: _*).mapN((_,_) => true) 

Can you help me to resolve this error?

Thanks for your help.

 


Many years ago I wrote a long blog post about the underlying issue you're running into here, if you're interested in the history or some old workarounds, but luckily now the solution is much easier (assuming you're on Scala 2.11 or 2.12): just add -Ypartial-unification to your Scala compiler options. If you're using sbt, for example, that might look like this:

scalacOptions += "-Ypartial-unification" 

And you're done.

If for some reason you can't add the compiler option, you'll have to provide some type parameters explicitly. Here's a quick simplified version:

import cats.data.Validated, cats.implicits._   case class Foo(i: Int) {   type ValidationResult[A] = Validated[List[String], A]    def check: ValidationResult[Boolean] =     if (i < 0) Validated.invalid(List("bad")) else Validated.valid(true) }  case class Foos(values: List[Foo]) {   type ValidationResult[A] = Validated[List[String], A]    def dup: ValidationResult[Unit] = Validated.valid(())   def check: ValidationResult[Boolean] =     (dup, values.map(_.check).sequence).mapN((_, _) => true) } 

That will fail with the error you saw (assuming you haven't added -Ypartial-unification):

<console>:22: error: Cannot prove that cats.data.Validated[List[String],Boolean] <:< G[A].            (dup, values.map(_.check).sequence).mapN((_, _) => true)                                      ^ 

To fix it you can write the following:

case class Foos(values: List[Foo]) {   type ValidationResult[A] = Validated[List[String], A]    def dup: ValidationResult[Unit] = Validated.valid(())   def check: ValidationResult[Boolean] =     (dup, values.map(_.check).sequence[ValidationResult, Boolean]).mapN((_, _) => true) } 

I think you could also probably just move the type alias to the package level, but I'm not 100% sure about that and am not motivated to check, sorry.

One footnote: any time you have map and then sequence, you can make things a little faster by using traverse instead:

  def check: ValidationResult[Boolean] =     (dup, values.traverse[ValidationResult, Boolean](_.check)).mapN((_, _) => true) 

Again you can drop the type parameters and let type inference figure them out if you have -Ypartial-unification enabled.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: