I have a question related to implicit priority system. I have following code:
object MyMain extends App {
case class Plain(s: String)
// cats version
trait CatShow[T] extends ContravariantShow[T]
object CatShow {
def apply[A](implicit instance: CatShow[A]): CatShow[A] = instance
trait ContravariantShow[-T] extends Serializable {
def show(t: T): String
}
def show[A](f: A => String): CatShow[A] = new CatShow[A] {
def show(a: A): String = f(a)
}
}
// my simplified version
trait MyShow[-T] extends Serializable {
def show(t: T): String
}
object MyShow {
def apply[A](implicit instance: MyShow[A]): MyShow[A] = instance
def show[A](f: A => String): MyShow[A] = new MyShow[A] {
def show(a: A): String = f(a)
}
}
// implicits definition for both
abstract class ImplicitInstances0 extends ImplicitInstances1 {
implicit val catShowPlain: CatShow[Plain] = CatShow(_.toString + "[cat-plain]")
implicit val myShowPlain: MyShow[Plain] = MyShow(_.toString + "[my-plain]")
}
abstract class ImplicitInstances1 {
implicit val catShowAny: CatShow[Any] = CatShow(_.toString + "[cat-Any]")
implicit val myShowAny: MyShow[Any] = MyShow(_.toString + "[my-Any]")
}
object ImplicitInstances extends ImplicitInstances0
import ImplicitInstances._
implicitly[CatShow[Plain]] // works magically
implicitly[MyShow[Plain]] // [ERROR] compiler error for ambiguous implicit
}
just wonder why the ContravariantShow
will help the complier for prioritisation. Ideally, I would like to go through 2 cases step to step to show why one works and the other one fails.
Thanks
just wonder why the
ContravariantShow
will help the complier for prioritisation.
ContravariantShow
doesn't help for prioritisation. If you remove it implicit will still resolve.
trait CatShow[T] /*extends ContravariantShow[T]*/ {
def show(t: T): String // added
}
object CatShow {
def apply[A](implicit instance: CatShow[A]): CatShow[A] = instance
// trait ContravariantShow[-T] extends Serializable {
// def show(t: T): String
// }
def show[A](f: A => String): CatShow[A] = new CatShow[A] {
def show(a: A): String = f(a)
}
}
What is important is variance of type class. CatShow
is invariant and MyShow
is contravariant. When you're looking for implicitly[CatShow[Plain]]
only catShowPlain
is a candidate. When you're looking for implicitly[MyShow[Plain]]
both myShowAny
(because implicitly[MyShow[Any] <:< MyShow[Plain]]
) and myShowPlain
are candidates. And they make ambiguity because of the reason Why is this implicit ambiguity behaviour happening?