2

I want to define equality for some type that can be part of other objects or collections using cats/kitten. I don't want to have to define the equality for every other class. For example:

import cats.Eq

case class Foo(a: Int, bar: Bar)

case class Bar(b: Int)

object BarEq {
  implicit val barEq: Eq[Bar] = Eq.instance[Bar] {
    (b1, b2) => b1.b +1 == b2.b
  }
}

and then a test defined as

import cats.derived.auto.eq._


class BarEqTest extends FlatSpec with cats.tests.StrictCatsEquality {
  import BarEq._

  "bareq" should {
    "work" in {
      Foo(1, Bar(1)) should ===(Foo(1, Bar(2)))
      Bar(1) should ===(Bar(2))
      Some(Foo(1, Bar(1))) should ===(Some(Foo(1, Bar(2))))
    }
  }
}

this works fine, but if I try to add the following test case

Seq(Foo(1, Bar(1))) should ===(Seq(Foo(1, Bar(2))))

I get

[Error] types Seq[Foo] and Seq[Foo] do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.CanEqual[Seq[Foo],Seq[Foo]]
one error found

How come the auto derived eq works with Option but not Seq, and how can I make it work? I tried to add import cats.instances.seq._ but that did not work either.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • 2
    Note that your equality operation is not commutative, if `bar1 === b2`, then `b2 !== b1`. – Gaël J Oct 16 '21 at 09:02
  • 1
    Can't reproduce https://scastie.scala-lang.org/DmytroMitin/jvhZl0xsT1KSVqB7hRNqEQ/2 Write your `build.sbt` (versions of Scala, Cats, Kittens), imports etc. – Dmytro Mitin Oct 16 '21 at 11:03
  • 1
    I am using scala 2.12, seems it is not working in the link you provided as well if I set it to 2.12. https://scastie.scala-lang.org/MtqPNcPDRAKe42P2kWIu9g – worldwideop Oct 16 '21 at 11:58

1 Answers1

4

Cats defines an instance of Eq for scala.collection.immutable.Seq, not for generic scala.collection.Seq (or moreover scala.collection.mutable.Seq)

import scala.collection.immutable.{BitSet, Queue, Seq, SortedMap, SortedSet}

private[kernel] trait EqInstances0 {
  implicit def catsKernelEqForSeq[A: Eq]: Eq[Seq[A]] = cats.kernel.instances.seq.catsKernelStdEqForSeq[A]
}

https://github.com/typelevel/cats/blob/main/kernel/src/main/scala/cats/kernel/Eq.scala#L283-L285

Starting from Scala 2.13.0 scala.Seq is scala.collection.immutable.Seq. But in Scala 2.12.x scala.Seq is scala.collection.Seq.

https://github.com/scala/scala/releases/tag/v2.13.0

So in Scala 2.12 import correct collection type

import scala.collection.immutable.Seq
Seq(Foo(1, Bar(1))) === Seq(Foo(1, Bar(2))) // true

or define your own instance of Eq for necessary collections

import cats.kernel.instances.StaticMethods

implicit def genericSeqEq[A: Eq]: Eq[collection.Seq[A]] = new Eq[collection.Seq[A]] {
  override def eqv(xs: collection.Seq[A], ys: collection.Seq[A]): Boolean =
    if (xs eq ys) true
    else StaticMethods.iteratorEq(xs.iterator, ys.iterator)
}

Seq(Foo(1, Bar(1))) === Seq(Foo(1, Bar(2))) // true
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66