Scala: неявный приоритет разрешения параметров - PullRequest
14 голосов
/ 24 декабря 2011

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

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 гласит:

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

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

Ответы [ 3 ]

16 голосов
/ 01 января 2012

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

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

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

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

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

7 голосов
/ 24 декабря 2011

С http://www.scala -lang.org / documents / files / ScalaReference.pdf , глава 2:

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

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

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

1 голос
/ 25 декабря 2011

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

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

...