I'm trying out cats library but I have hard time navigating between things I should import and create. My problem is as follows:
sealed trait Checks
case class CheckViolation(id: Long, msg: String) extends Checks
case class ChecksPassed(ids: Seq[Long]) extends Checks
This is my data structures I want to work with. Every violation should be held as object with msg, checks passed can be aggregated to hold only ids.
object BusinessRuleSetValidation extends App {
type BRValidationResult = Validated[NonEmptyList[CheckViolation], ChecksPassed]
def run(): BRValidationResult = {
implicit val monoidChecksPassed = new Monoid[ChecksPassed] {
override def empty: ChecksPassed = ChecksPassed(Seq())
override def combine(x: ChecksPassed, y: ChecksPassed): ChecksPassed = ChecksPassed(x.ids ++ y.ids)
}
val check1: BRValidationResult = valid(ChecksPassed(2L))
val check2: BRValidationResult = invalidNel(CheckViolation(1, "This check was violated"))
val check3: BRValidationResult = invalidNel(CheckViolation(2, "This is a violation"))
val p = Foldable[NonEmptyList].fold(NonEmptyList(check1, check2, check3))
The last fold
leads to an compilation error.
BusinessRuleSetValidation.scala:48: could not find implicit value for parameter A: cats.Monoid[cats.data.Validated[cats.data.OneAnd[com.adform.br.server.model.validation.CheckViolation,[+A]List[A]],com.adform.br.server.model.validation.ChecksPassed]]
[error] val p = Foldable[NonEmptyList].fold(NonEmptyList(check1, check2, check3))
NonEmptyList
should a perfer candidate for folding. Validated combine should also be there. As for my classes, ChechViolation
is in NonEmptyList
so it does not need a monoid instance. For the ChecksPassed
I've created a monoid instance so I don't really get what is missing here.
EDIT
I did not include my imports and they are important here:
import cats._
import cats.data.Validated._
import cats.data.{NonEmptyList, Validated, Xor}
import cats.data.OneAnd.oneAndFoldable
import cats.std.all._
import cats.syntax.apply._
import cats.syntax.order._
import cats.syntax.xor._
import cats.syntax.semigroup._
Ok, I figured it out.
So I leave an answer maybe some one will find it helpful.
There is no possibility to have a Monoid for NonEmptyList. Why? Monoid needs a neutral element, and for list that would be empty, but our list does not allow this.
So I've changed how checks are grouped from NEL to List.
It turned out that I also need to create a Monoid for Validation and it looked like this:
implicit val brValidationResutlMonoid = new Monoid[BRValidationResult] {
override def empty: BRValidationResult = valid(ChecksPassed(Seq.empty))
override def combine(x: BRValidationResult, y: BRValidationResult): BRValidationResult = (x,y) match {
case (Valid(a),Valid(b)) => valid(ChecksPassed(a.ids ++ b.ids))
case (Valid(_), k @ Invalid(_)) => k
case (f @ Invalid(_), Valid(_)) => f
case (Invalid(l1),Invalid(l2)) => Invalid(l1.combine(l2))
}
}
The types if you listen to them carefully guide you well ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With