0

This answer https://stackoverflow.com/a/56366311/2682459 shows how it is possible to use an object to provide a custom implementation of a typeclass when using Kitten. Applying the same principle to the following code though doesn't work:

package com.xxx.yyy.zzz

import cats._, cats.derived._, cats.implicits._

object Test extends App {

  case class Inner(double: Double)

  case class Outer(inner: Inner, s: String)

  implicit object doubleEq extends Eq[Double] {
    override def eqv(x: Double, y: Double): Boolean = Math.abs(x - y) < 0.1
  }

  implicit val outerEq: Eq[Outer] = {
    import derived.auto.eq._
    derived.semi.eq[Outer]
  }

  implicitly[Eq[Double]]

  val testCC1 = Outer(Inner(1.01d), "BlahBlahBlah")
  val testCC2 = Outer(Inner(1.00d), "BlahBlahBlah")

  println(testCC1 === testCC2)

}

The implicitly[Eq[Double]] shows that once again I have ambiguous implicits:

Error:(20, 13) ambiguous implicit values:
 both value catsKernelStdOrderForDouble in trait DoubleInstances of type => cats.kernel.Order[Double] with cats.kernel.Hash[Double]
 and object doubleEq in object Test of type com.xxx.yyy.zzz.Test.doubleEq.type
 match expected type cats.Eq[Double]
  implicitly[Eq[Double]]

How do I work round this one? I really don't want to have to cherry pick the cats implicits I import as this isn't very scalable!

user2682459
  • 999
  • 2
  • 13
  • 24

2 Answers2

2

Change your imports. Don't import cats.instances.double._

import cats._, cats.derived._
import cats.instances.string._ // Outer uses Double and String
import cats.syntax.eq._ // for ===

Both

implicit val doubleEq: Eq[Double] = new Eq[Double] {
  override def eqv(x: Double, y: Double): Boolean = Math.abs(x - y) < 0.1
}

and

implicit object doubleEq extends Eq[Double] {
  override def eqv(x: Double, y: Double): Boolean = Math.abs(x - y) < 0.1
}

work.

http://eed3si9n.com/herding-cats/import-guide.html

https://blog.softwaremill.com/9-tips-about-using-cats-in-scala-you-might-want-to-know-e1bafd365f88 advice 2)

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Nice! I was hoping there would be a way to not have to manage the cats imports so tightly. Aiming to construct something that works reliably irrespective of whether it is doubles I’m overriding or another type/types , without having to change the imports. Since there’s a hierarchy to implicits thought this would be possible? – user2682459 May 30 '19 at 07:25
  • Well, you can import `cats.syntax.all._` instead of `cats.syntax.eq._` since ambiguity is in instances and not syntaxes. – Dmytro Mitin May 30 '19 at 07:52
  • It's more that I don't want to have to import cats.instances.long, cats.instances.xxx whenever my case class changes, as I want a solution which works for any case class – user2682459 May 30 '19 at 08:59
1

A bit more playing - this seems to give me what I want:

import cats._
import cats.derived._
import cats.syntax.eq._
import derived.auto.eq._

object CustomImplicits extends cats.instances.AllInstances {
  implicit object doubleEq extends Eq[Double] {
    def eqv(x: Double, y: Double): Boolean = Math.abs(x - y) < 0.1
  }
}

object Test extends App {

  case class Inner(double: Double)

  case class Outer(inner: Inner, s: String)

  import CustomImplicits._

  implicitly[Eq[Double]]

  val testCC1 = Outer(Inner(1.01d), "BlahBlahBlah")
  val testCC2 = Outer(Inner(1.00d), "BlahBlahBlah")
  val testCC3= Outer(Inner(2.00d), "BlahBlahBlah")

  println(testCC1 === testCC2) //True
  println(testCC2 === testCC3) //False

}
user2682459
  • 999
  • 2
  • 13
  • 24