Этот ответ https://stackoverflow.com/a/56366311/2682459 показывает, как можно использовать объект для предоставления пользовательской реализации класса типов при использовании Kitten. Применение того же принципа к следующему коду не работает:
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)
}
implicitly[Eq[Double]]
показывает, что у меня снова неоднозначные имплициты:
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]]
Как мне обойти это? Я действительно не хочу выбирать неявные атрибуты кошек, которые я импортирую, поскольку это не очень масштабируемо!
Измените свой импорт. Не импортировать cats.instances.double._
import cats._, cats.derived._
import cats.instances.string._ // Outer uses Double and String
import cats.syntax.eq._ // for ===
Обе
implicit val doubleEq: Eq[Double] = new Eq[Double] {
override def eqv(x: Double, y: Double): Boolean = Math.abs(x - y) < 0.1
}
а также
implicit object doubleEq extends Eq[Double] {
override def eqv(x: Double, y: Double): Boolean = Math.abs(x - y) < 0.1
}
работай.
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 совет 2)
Что ж, вы можете импортировать cats.syntax.all._
вместо cats.syntax.eq._
, поскольку неоднозначность заключается в экземплярах, а не в синтаксисе.
Дело в том, что я не хочу импортироватьcats.instances.long,cats.instances.xxx всякий раз, когда изменяется класс моего случая, так как мне нужно решение, которое работает для любого класса случая.
Еще немного игры - кажется, это дает мне то, что я хочу:
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
}
Хороший! Я надеялся, что будет способ не так жестко контролировать импорт кошек. Стремясь создать что-то, что надежно работает независимо от того, переопределяю ли я двойники или другой тип/типы, без необходимости изменять импорт. Поскольку существует иерархия имплицитов, это возможно?