Dado el siguiente objeto acompañante con versiones sobrecargadas de apply:

object List {
  def apply[T](): List[T] = new Nil
  def apply[T](x1: T): List[T] = new Cons(x1, new Nil)
  def apply[T](x1: T, x2: T): List[T] = new Cons(x1, new Cons(x2, new Nil))
  def apply[T](elems: T*): List[T] = 
    elems.foldRight(List[T])((elem, l) => new Cons(elem, l))
}

Y los dos casos

List(1) //Error - Ambiguity 
List('a', 'b') //Works fine

scalac se queja acerca de la primera instanciación (referencia ambigua a una sobrecarga en la definición de) debido a que tanto el único argumento y la varargs método son igualmente .

La búsqueda de stackoverflow he encontrado que es posible la fuerza del argumento único método. List[Int](1) hará que el compilador de uso def apply[T](x1: T).

Mi pregunta es ¿por qué el segundo la creación de instancias partido def apply[T](x1: T, x2: T) sin extra «sugerencias»? En otras palabras, ¿por qué los dos argumentos método más específicos de la varargs método donde el único argumento método ¿no?

OriginalEl autor Anthony Accioly | 2013-05-30

2 Comentarios

  1. 7

    Para responder a su pregunta tenemos que echar un vistazo a lo que sucede cuando el compilador de Scala tiene que realizar la sobrecarga de resolución. Esto se describe en el SLS 6.23.3 (para Scala 2.9).

    Vamos a tomar un poco más simple versión de su ejemplo:

    object Test {
      def apply[T](x1: T) = "one arg"                      //A
      def apply[T](x1: T, x2: T) = "two args"              //B
      def apply[T](elems: T*) = "var-args: " + elems.size  //C
    }

    Y mira estas tres llamadas:

    Test(1) //fails, ambiguous reference, A and C both match arguments
    Test[Int](1) //returns "one arg"
    Test(1,2) //returns "two args", not "var-args: 2"

    Vamos a empezar con la primera. En primer lugar, el compilador busca en el forma de cada argumento, que es un tipo que se describe, básicamente, si el argumento es un valor o una función. Aquí, sin dificultad, 1 es muy normal, aburrido valor y su forma es la de tipo Nothing.

    Ahora tiene un solo argumento 1, de tipo Nothing, y busca todas las alternativas que les son aplicables. Encuentra dos de ellos:

    • apply[T](x1: T): se toma un único argumento de unbounded tipo, por lo que puede recibir un argumento de tipo Nothing,
    • apply[T](elems: T*): puede ser aplicado a cualquier número (0 incluido) de argumentos del mismo unbounded tipo, por lo que puede recibir un solo elemento de tipo Nothing.

    Que sólo había uno, iba a parar allí y elegir que uno.

    El segundo paso es idéntica a la anterior, excepto que esta vez es de los tipos de cada uno de los argumentos con un indefinido tipo esperado. Básicamente aquí se ve a las dos alternativas de la izquierda y averigua si son aplicables al argumento 1 de tipo A <: Int. No hubo suerte, ambos son.
    Si fueron dos cambios apply[T](x1: T) a apply(x1: String) y deja que el otro solo, aquí hay sólo sería aplicable alternativa de la izquierda y de que tendría éxito y parada.

    Entonces el compilador calcula el relative weight de cada alternativa de la izquierda sobre la otra. El SLS estados que

    La peso relativo de una alternativa a sobre la alternativa B es un número de 0 a 2,
    se define como la suma de

    • 1 si a es tan específica como la de B , 0 en caso contrario, y
    • 1 si a es definida en una clase u objeto del cual se deriva de la clase o el objeto
      la definición de B , 0 en caso contrario.

    En este punto, debe haber una alternativa que tenga una puntuación superior a todos los demás, o hay un error de ambigüedad. Podemos ignorar el «definido», son definidos en el mismo lugar.

    • A es tan específico como C porque siempre puede llamar a C con el único argumento de A,
    • C es tan específico como A porque de inferencia de tipos: siempre puede llamar a A con el argumento de C desde A puede tomar nada (su tipo del parámetro que se puede inferir a lo que queramos). C‘s parámetros es visto como un Seq[A] así T se infiere como Seq[A] en A y se puede llamar. Así C es tan específico como A.

    Esto se puede ver si cambia A a apply[T <: Int](x: T): se va todo el camino a la búsqueda de los más específicos, pero esta vez de tipo de inferencia no puede encontrar una manera de hacer A aplicable a C‘s argumento (un Seq) porque no es un subtipo de Int, así A es la más específica. Obviamente, lo mismo sucede si cambia A a apply(x: Int), la inferencia de tipos aún no se puede hacer nada.

    Esto también explica por qué Test[Int](1) logra llamar el argumento de la versión. Las dos últimas alternativas son idénticos, pero A‘s parámetro de tipo ha sido obligado a Int y el tipo de inferencia no puede cambiar para que se ajuste a C‘s argumento más.

    Finalmente, aplicando la misma lógica, por qué Test(1,2) funciona bien:

    • B es tan específico como C: siempre puede llamar a C con B‘s argumentos,
    • pero C es no tan específico como B: no cantidad de inferencia de tipos va a administrar a un único Seq en un método que toma dos parámetros.

    Así apply[T](x1: T, x2: T) es el más específico y no hay errores.

    Básicamente para un var-arg y un método normal para producir una ambigüedad, que tienen que tener el mismo número de argumentos y una forma de engañar a la inferencia de tipos en (al menos) el último argumento:

    def apply[T](x1: T)
    def apply[T](x: T*)

    O

    def apply[T](x1: Int, x2:T)
    def apply[T](x1: Int, x: T*)

    Y así sucesivamente…

    Editar: yo no estaba seguro de en primer lugar si los repetidos parámetro fue visto como un Seq[A] o un TupleX[...] cuando se busca la especificidad. Definitivamente no es una tupla, y auto-tupling no tiene nada que ver con nada de esto.

    «la intersección de la sobrecarga, el polimorfismo y la inferencia de tipos»: no deje que esto paulpism inducir a error. Él sólo quería decir que si el compilador actuó como no tiene que deducir nada, porque es como que obvio a partir de la func aplicación, entonces usted no tiene que proporcionar el tipo de arg.
    oh, ya veo. Estoy triste, pensé que él significaba más general, que cuando estos 3 ortogonal conceptos se cruzan, cosas extrañas pueden suceder.

    OriginalEl autor gourlaysama

  2. 3

    Fijo arity método siempre es más específico que el var-arity.

    f(P1, P2) no se aplica a (a, b, c, ...), que es como usted puede pensar de f(P*).

    Por el contrario, aunque, f(P*) toma la forma f(p1,..,pn) para los fines de la aplicabilidad a N argumentos. Por lo que siempre se aplica y no es tan específico como el fijo-arity método.

    Así que esa es la razón normal de su f(a,b) es más específica que la f(P*).

    Para el uno-arg caso, depende de lo que usted escoja para el tipo param.

    f[A](a: A) aplica a (a, b, ...) por tupling y tomando como una Tupla.

    Diciendo A = Int, entonces, evidentemente, Una no puede ser tomado como una Tupla.

    Muestra de la confusión acerca de var-arity y cómo afecta a la especificidad:

    https://issues.scala-lang.org/browse/SI-4728

    object Foo {
      def apply[T](): Int = 1
      def apply[T](x1: T): Int = 2
      def apply[T](x1: T, x2: T): Int = 3
      def apply[T](elems: T*): Int = 4
    
      //two or more
      def canonically[T](x1: T, x2: T, rest: T*): List[T] = ???
    }
    
    object Test extends App {
      Console println Foo(7)
      Console println Foo[Int](7)
      Console println Foo(7,8)
      Console println Foo[Pair[Int,Int]](7,8)
    }

    Usted podría desea publicar esta pregunta en stackoverload.com el sitio donde la sobrecarga de especialistas se reúnen. Su navegador puede ser redirigido a overloading-is-evil.com.

    No entiendo cómo este responde a la OPs pregunta. Por favor elaborados.
    som, gracias por la explicación. Sólo en aras de la claridad.. En Foo(x1: T), T podría ser un Tuple, tuplas y varargs son igualmente específicas, es por eso que necesito sugerencia al compilador que T no es una tupla de la derecha?
    Sí, «es tan específica como la de» es una prueba de si un método se aplica a la (hipotética) argumentos de los otros. Si el parámetro es de tipo T o Ninguna, entonces usted puede tupla su camino en lo que hace que ni «más específicas» que el otro. La forma canónica para 2 o más no tiene ese problema, pero es más difícil de usar. Mi rapidito idea una vez que se tenga la sintaxis f(two_plus: {2,}) así que dentro de la func sólo lidiar con Seq.
    Thx @gzm0, yo estaba corriendo hacia la puerta de jardín de infantes a la primera.

    OriginalEl autor som-snytt

Dejar respuesta

Please enter your comment!
Please enter your name here