Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fold on NonEmptyList

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._
like image 733
almendar Avatar asked Sep 09 '15 10:09

almendar


1 Answers

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 ;)

like image 100
almendar Avatar answered Sep 21 '22 20:09

almendar