conversion - Scala: precedencia de resolución implícita de parámetros
scala implicit conversion (3)
¿Podría alguien explicar cómo se considera más específico usando "las reglas de la resolución de sobrecarga estática (§6.26.3)"?
No hay sobrecarga de método, por lo que 6.26.3 es totalmente irrelevante aquí.
La sobrecarga se refiere a múltiples métodos con el mismo nombre pero diferentes parámetros definidos en la misma clase. Por ejemplo, el método f
en el ejemplo 6.26.1 está sobrecargado:
class A extends B {}
def f(x: B, y: B) = . . .
def f(x: A, y: B) = . . .
val a: A
val b: B
La precedencia de resolución de parámetro implícita es una regla completamente diferente, y una que ya tiene una pregunta y respuesta en Desbordamiento de pila.
Supongamos que tenemos una búsqueda de parámetros implícita solo con respecto a los ámbitos locales:
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)
}
En el código anterior, LocalIntFoo
gana sobre ImportedIntFoo
. ¿Podría alguien explicar cómo se considera más específico usando "las reglas de la resolución de sobrecarga estática (§6.26.3)"?
Editar :
El nombre de prioridad de enlace es un argumento convincente, pero hay varios problemas sin resolver. Primero, la referencia del lenguaje Scala dice:
Si hay varios argumentos elegibles que coinciden con el tipo del parámetro implícito, se elegirá el más específico utilizando las reglas de resolución de sobrecarga estática (§6.26.3).
En segundo lugar, la precedencia del enlace de nombre se trata de resolver un identificador x
conocido a un miembro particular pkg.ABx
en caso de que haya varias variables / métodos / objetos nombrados x
en el alcance. ImportIntFoo
y LocalIntFoo
no reciben el mismo nombre.
En tercer lugar, puedo mostrar que el nombre de prioridad de enlace por sí solo no está en juego de la siguiente manera:
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)
Ponlo en test.scala
y ejecuta scala test.scala
, e imprime ImportIntFoo:1
. Esto se debe a que la resolución de sobrecarga estática (§6.26.3) dice que gana un tipo más específico. Si pretendemos que todos los valores implícitos elegibles reciben el mismo nombre, LocalAnyFoo
debería haber enmascarado ImportIntFoo
.
Relacionado :
Este es un gran resumen de la resolución de parámetros implícitos, pero cita la presentación de nescala de Josh en lugar de la especificación. Su charla es lo que me motivó a analizar esto.
Implementación del compilador
De http://www.scala-lang.org/docu/files/ScalaReference.pdf , Capítulo 2:
Los nombres en Scala identifican tipos, valores, métodos y clases que colectivamente se llaman entidades. Los nombres son introducidos por de fi niciones y declaraciones locales (§4), herencia (§5.1.3), cláusulas de importación (§4.7) o cláusulas de paquete (§9.2) que se denominan colectivamente vinculaciones.
Los enlaces de diferentes tipos tienen una precedencia definida en ellos: 1. Las definiciones y declaraciones que son locales, heredadas o disponibles por una cláusula de paquete en la misma unidad de compilación en donde se produce la definición tienen la más alta prioridad. 2. Las importaciones explícitas tienen la siguiente prioridad más alta. 3. Las importaciones de comodines tienen la siguiente prioridad más alta. 4. Las definiciones puestas a disposición por una cláusula de paquete que no está en la unidad de compilación en donde se produce la definición tienen la prioridad más baja.
Puede que me equivoque, pero la llamada a foo (1) está en la misma unidad de compilación que LocalIntFoo, lo que hace que la conversión tenga prioridad sobre ImportedIntFoo.
Escribí mi propia respuesta en forma de una publicación de blog revisitando implicits sin impuestos de importación .
Actualización : Además, los comentarios de Martin Odersky en la publicación anterior revelaron que el comportamiento de Scala 2.9.1 de LocalIntFoo
ganar ImportedIntFoo
es, de hecho, un error. Ver precedencia de parámetro implícita nuevamente .
- 1) implicaciones visibles para el ámbito de invocación actual mediante declaración local, importaciones, ámbito externo, herencia, objeto de paquete a los que se puede acceder sin prefijo.
- 2) ámbito implícito , que contiene todo tipo de objetos complementarios y objetos de paquete que guardan cierta relación con el tipo implícito que buscamos (es decir, objeto de paquete del tipo, objeto complementario del tipo en sí, de su tipo constructor, si lo hay, de sus parámetros si los hay, y también su supertipo y supertraits).
Si en cualquier etapa encontramos más de una regla de sobrecarga estática implícita, se usa para resolverla.
Actualización 2 : Cuando le pregunté a Josh sobre Implicits without Import Tax, me explicó que se estaba refiriendo a las reglas de vinculación de nombres para implicits que se llaman exactamente iguales .