Scala: Неявный приоритет разрешения параметров

Предположим, что мы имеем неявный поиск параметров, относящийся только к локальным областям:

trait CanFoo[A] {
  def foos(x: A): String
}

object Def {
  implicit object ImportIntFoo extends CanFoo[Int] {
    def foos(x: Int) = "ImportIntFoo:" + x.toString
  }
}

object Main {
  def test(): String = {
    implicit object LocalIntFoo extends CanFoo[Int] {
      def foos(x: Int) = "LocalIntFoo:" + x.toString
    }
    import Def._

    foo(1)
  }

  def foo[A:CanFoo](x: A): String = implicitly[CanFoo[A]].foos(x)
}

В приведенном выше коде LocalIntFoo побеждает над ImportedIntFoo. Может ли кто-нибудь объяснить, как он более конкретно рассматривается, используя "правила статического перегрузочного разрешения (§6.26.3)"?

Edit

Приоритет привязки имени является убедительным аргументом, но есть несколько нерешенных проблем. Во-первых, Scala Справочник по языку говорит:

Если имеется несколько подходящих аргументов, которые соответствуют типу неявных параметров, наиболее конкретный будет выбран с использованием правил статического перегрузочного разрешения (§6.26.3).

Во-вторых, приоритет привязки имени заключается в разрешении известного идентификатора x на конкретный элемент pkg.A.B.x в случае, если в области видится несколько переменных/методов/объектов с именем x. ImportIntFoo и LocalIntFoo не называются одинаковыми.

В-третьих, я могу показать, что приоритет привязки имени не входит в игру следующим образом:

trait CanFoo[A] {
  def foos(x: A): String
}

object Def {
  implicit object ImportIntFoo extends CanFoo[Int] {
    def foos(x: Int) = "ImportIntFoo:" + x.toString
  }
}

object Main {
  def test(): String = {
    implicit object LocalAnyFoo extends CanFoo[Any] {
      def foos(x: Any) = "LocalAnyFoo:" + x.toString
    }

    // implicit object LocalIntFoo extends CanFoo[Int] {
    //   def foos(x: Int) = "LocalIntFoo:" + x.toString
    // }
    import Def._

    foo(1)
  }

  def foo[A:CanFoo](x: A): String = implicitly[CanFoo[A]].foos(x)
}

println(Main.test)

Поместите это в test.scala и запустите scala test.scala, и он выведет ImportIntFoo:1. Это связано с тем, что статическое перегрузочное разрешение (§6.26.3) говорит о более конкретных типах выигрышей. Если мы притворяемся, что все подходящие неявные значения называются одинаковыми, LocalAnyFoo должен быть замаскирован ImportIntFoo.

Похожие

Это отличное резюме неявного разрешения параметров, но оно цитирует презентацию Josh nescala вместо спецификации. Его разговор побудил меня взглянуть на это.

Внедрение компилятора

Ответ 1

Я написал свой собственный ответ в виде сообщения в блоге пересматривает implicits без налога на импорт.

Обновление. Кроме того, комментарии Мартина Одерского в приведенной выше статье показали, что поведение Scala 2.9.1 LocalIntFoo, победившего над ImportedIntFoo, на самом деле является ошибкой. См. неявный приоритет параметров.

  • 1) подразумевает видимость текущей области вызова через локальное объявление, импорт, внешнюю область, наследование, объект пакета, доступный без префикса.
  • 2) неявная область, содержащая все виды сопутствующих объектов и объект пакета, которые имеют некоторое отношение к неявному типу, который мы ищем (т.е. пакетный объект типа, сопутствующий объект самого типа его конструктора типа if любой, его параметров, если таковой имеется, а также его супертипа и супертрайтов).

Если на любом этапе мы обнаруживаем более одного неявного, используется статическое правило перегрузки.

Обновление 2. Когда я спросил Джоша о Implicits без налога на импорт, он объяснил мне, что он ссылается на правила привязки имени для имплицитов, которые называются точно такими же.

Ответ 2

От http://www.scala-lang.org/docu/files/ScalaReference.pdf, глава 2:

Имена в Scala определяют типы, значения, методы и классы, которые совместно именуемые сущностями. Имена вводятся локальными определениями и декларации (§4), наследование (§5.1.3), положения о поставках (§4.7) или (§9.2), которые в совокупности называются привязками.

Привязки разных видов имеют приоритет, определенный для них: 1. Определения и декларации, которые являются локальными, унаследованными или доступными в соответствии с предложением пакета в том же компиляционном блоке, где определение имеет наивысший приоритет. 2. Явный импорт имеет следующий высокий приоритет. 3. Импорт подстановочных знаков имеет следующий высокий приоритет. 4. Определения, предоставляемые положением пакета, не входящим в блок компиляции, где имеет место определение, имеют наименьший приоритет.

Я могу ошибаться, но вызов foo (1) находится в том же компиляционном блоке, что и LocalIntFoo, в результате чего преобразование имеет приоритет над ImportedIntFoo.

Ответ 3

Может кто-нибудь объяснить, как он более конкретно рассматривается с помощью "правила разрешения статической перегрузки (§6.26.3)"?

Нет никакой перегрузки метода, поэтому 6.26.3 здесь совершенно неактуальен.

Перегрузка относится к нескольким методам с тем же именем, но разные параметры определяются в одном классе. Например, метод f в примере 6.26.1 перегружен:

class A extends B {}
def f(x: B, y: B) = . . .
def f(x: A, y: B) = . . .
val a: A
val b: B

Неявный приоритет разрешения параметров - это совершенно другое правило, и тот, у кого есть вопрос и ответ уже на переполнение стека.