low priority and high priority implicits in scala

2019-08-25 02:37发布

问题:

In following code from Scala's puzzlers, it seems that there is no conflict in implicits because TestAlarmHandler is more specific. I didn't understand the explanation though. Why is TestAlarmHandler is more specific than DefaultAlarmHandler?

object Scanner {
  trait Console { def display(item: String) }
  trait AlarmHandler extends (() => Unit)
  def scanItem(item: String)(implicit c: Console) {
    c.display(item)
  }
  def hitAlarmButton()(implicit ah: AlarmHandler) { ah() }
}


class OperatingMode {
  implicit val ConsoleRenderer = new Scanner.Console {
    def display(item: String) { println(s"Found a ${item}") }
  }
  implicit val DefaultAlarmHandler = new Scanner.AlarmHandler {
    def apply() { println("ALARM! ALARM!") }
  }
}
object NormalMode extends OperatingMode
object TestMode extends OperatingMode {
  override implicit val ConsoleRenderer = new Scanner.Console {
    def display(item: String) { println("Found a detonator") }
  }
  implicit val TestAlarmHandler = new Scanner.AlarmHandler {
    def apply() { println("Test successful. Well done!") }
  }
}
import NormalMode._
scala> Scanner scanItem "knife"
Found a knife
scala> Scanner.hitAlarmButton()
ALARM! ALARM!
import TestMode._
scala> Scanner scanItem "shoe"
Found a detonator
scala> Scanner.hitAlarmButton()
Test successful. Well done!

回答1:

TestAlarmHandler is more specific than DefaultAlaramHandler because TestAlarmHandler is defined in an anonymous class which is derived from class OperatingMode defining DefaultAlarmHandler.


Formally, the Scala Language Specification specifies how to handle a case when more than one eligible arguments are found, as follows (note that both TestAlarmHandler and DefaultAlarmHandler are eligible because they have been imported by import TestMode._):

If there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static overloading resolution.

Section 6.26.3 Overloading Resolution defines the concept of being more specific in terms of the concept of relative weight:

The relative weight of an alternative A over an alternative B is a number from 0 to 2, defined as the sum of

  • 1 if A is as specific as B, 0 otherwise, and
  • 1 if A is defined in a class or object which is derived from the class or object defining B, 0 otherwise.

The relative weight of TestAlarmHandler over DefaultAlarmHandler is 1 while that of DefaultAlarmHandler over TestAlarmHandler is 0. Note that TestAlarmHandler is defined in an anonymous class which is derived from class OperatingMode defining DefaultAlarmHandler. The first rule is of concern when resolving method overloading, but not relevant here.

An alternative A is more specific than an alternative B if the relative weight of A over B is greater than the relative weight of B over A.

TestAlarmHandler is more specific than DefaultAlaramHandler because the relative weight TestAlarmHandler over DefaultAlarmHandler is greater than that of the inverse.



回答2:

That's because TestMode is subclass of OperationMode, and scala at first look for implicits in scope of TestMode and only if not found it will lookup up in hierarchy.