Can I tell scala how to prefer more specific implicit, rather than give "ambiguous implicit" error?

StackOverflow https://stackoverflow.com/questions/19968114

  •  30-07-2022
  •  | 
  •  

Вопрос

The following code gives an error.

class Base { }
class Sub extends Base { }

class Writer[-T] {
  def write(t: T) { }
}

object AmbImplicits {

  implicit object BaseWriter extends Writer[Base]
  implicit object SubWriter extends Writer[Sub]

  def foo[T](t: T)(implicit ev: Writer[T]) {
    println(s"foo $t $ev")
  }

  def main(args: Array[String]) {
    val base = new Base
    val sub = new Sub
    foo(base)
    foo(sub)
  }

}

The error:

/Workspace/AmbImplicits.scala:24: error: ambiguous implicit values:
both object SubWriter in object AmbImplicits of type AmbImplicits.SubWriter.type
and object BaseWriter in object AmbImplicits of type AmbImplicits.BaseWriter.type
match expected type Writer[Sub]
    foo(sub)
       ^

In my real code it is not so simple to explicitly pass the implicit parameter. Is there way to tell it to always prefer SubWriter over BaseWriter, because the former is more specific? Without manually passing it like foo(sub)(SubWriter)?

Это было полезно?

Решение

If you can't do what Shadowlands suggests due to Base and Sub being Java types, you can just reduce the priority of the BaseWriter implicit by moving it up the inheritance chain:

trait LowPriorityImplicits { implicit object BaseWriter extends Writer[Base] }

object AmbImplicits extends LowPriorityImplicits {
  implicit object SubWriter extends Writer[Sub]

  def foo[T](t: T)(implicit ev: Writer[T]) {
    println(s"foo $t $ev")
  }

  def main(args: Array[String]) {
    val base = new Base
    val sub = new Sub
    foo(base)
    foo(sub)
  }
}

Другие советы

If you are able to, put the implicits in companion objects for each type derived from Base, rather than in the object with the foo method:

class Base { }
object Base { implicit object BaseWriter extends Writer[Base] }
class Sub extends Base { }
object Sub { implicit object SubWriter extends Writer[Sub] }

object NotAmbImplicits {

  def foo[T](t: T)(implicit ev: Writer[T]) {
    println(s"foo $t $ev")
  }

  def main(args: Array[String]) {
    val base = new Base
    val sub = new Sub
    foo(base)
    foo(sub)
  }

}

The compiler will look in the companion object for a given T passed to foo to see if it can use an implicit from there.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top