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)"?

Редактировать:

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

Если имеется несколько подходящих аргументов, которые соответствуют типу неявного параметра, наиболее определенный из них будет выбран с использованием правил разрешения статической перегрузки (§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,

Связанные:

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

Реализация компилятора

  • rankImplicits

3 ответа

Решение

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

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

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

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

Обновление 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.

Может ли кто-нибудь объяснить, как это считается более конкретным, используя "правила разрешения статической перегрузки (§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

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

Другие вопросы по тегам