2011-01-10 16 views
105

Si bien puede haber casos válidos donde tales sobrecargas de métodos podrían volverse ambiguas, ¿por qué el compilador no permite código que no es ambiguo en tiempo de compilación ni en tiempo de ejecución?¿Por qué el compilador Scala no permite métodos sobrecargados con argumentos predeterminados?

Ejemplo:

// This fails: 
def foo(a: String)(b: Int = 42) = a + b 
def foo(a: Int) (b: Int = 42) = a + b 

// This fails, too. Even if there is no position in the argument list, 
// where the types are the same. 
def foo(a: Int) (b: Int = 42) = a + b 
def foo(a: String)(b: String = "Foo") = a + b 

// This is OK: 
def foo(a: String)(b: Int) = a + b 
def foo(a: Int) (b: Int = 42) = a + b  

// Even this is OK. 
def foo(a: Int)(b: Int) = a + b 
def foo(a: Int)(b: String = "Foo") = a + b 

val bar = foo(42)_ // This complains obviously ... 

¿Hay alguna razón por estas restricciones no se puede aflojar un poco?

Especialmente cuando la conversión de código Java sobrecargado a Scala los argumentos predeterminados son muy importantes y no es agradable averiguar después de reemplazar un montón de métodos de Java por un método de Scala que la especificación/compilador impone restricciones arbitrarias.

+11

* "restricciones arbitrarias" * :-) – KajMagnus

+1

Parece que puede evitar el problema con los argumentos de tipo. Esto compila: 'object Test {def a [A] (b: Int, c: Int, d: Int = 7): Unit = {}; def a [A] (a: String, b: String = ""): Unit = {}; a (2,3,4); a ("a");} ' – user1609012

+0

@ user1609012: Tu truco no funcionó para mí. Lo probé usando Scala 2.12.0 y Scala 2.11.8. –

Respuesta

77

me gustaría citar Lukas Rytz (de here):

La razón es que queríamos una denominación esquema determinista para los métodos generados cuales devolver argumentos por defecto.Si se escribe

def f(a: Int = 1)

el compilador genera

def f$default$1 = 1

Si tiene dos sobrecargas con incumplimientos en la misma posición parámetro , necesitaríamos un esquema de nombres diferentes. Pero queremos mantener el código de bytes generado estable sobre múltiples ejecuciones del compilador.

Una solución para la futura versión Scala podría ser incorporar nombres de tipo de los argumentos no predeterminados (los que están en el comienzo de un método, que desambiguar versiones sobrecargado) en el esquema de nomenclatura, por ejemplo, en este caso:

def foo(a: String)(b: Int = 42) = a + b 
def foo(a: Int) (b: Int = 42) = a + b 

sería algo así como:

def foo$String$default$2 = 42 
def foo$Int$default$2 = 42 

Alguien dispuesto a write a SIP proposal?

+1

Creo que su propuesta aquí tiene mucho sentido, y no veo qué sería tan complejo sobre especificarla/implementarla. Esencialmente, los tipos de parámetros son parte de la identificación de la función. ¿Qué hace el compilador actualmente con foo (String) y foo (Int) (es decir, métodos sobrecargados SIN un valor predeterminado)? – Mark

+0

¿No introduciría esto la notación húngara obligatoria al acceder a los métodos de Scala desde Java? Parece que haría las interfaces extremadamente frágiles, lo que obligaría al usuario a tener cuidado al cambiar los parámetros de las funciones. –

+0

Además, ¿qué pasa con los tipos complejos? 'A con B', por ejemplo? –

-2

Si llamó al foo() ¿cuál debería invocar?

+0

Eso debería ser un error del compilador, obviamente. Pero no puedo ver cómo esto está relacionado con la pregunta? – soc

+0

@soc Parecería obvio que si el compilador no puede determinar cuál debería invocarse, entonces no sería permitido. Sin embargo, dicho esto, he leído mal el código en la pregunta. Si los métodos se vuelven ambiguos, parecería que no se permitiría. Nuevamente leí mal el código y solo vi un argumento, por lo que pregunté a foo(), no foo (100) –

+2

. Si su respuesta es para otra pregunta, ¿leyó mal por qué no solo la eliminó en lugar de decir que leyó mal? Mantengamos el sitio despejado. – sbeliakov

6

no puedo responder a su pregunta, pero aquí es una solución:

implicit def left2Either[A,B](a:A):Either[A,B] = Left(a) 
implicit def right2Either[A,B](b:B):Either[A,B] = Right(b) 

def foo(a: Either[Int, String], b: Int = 42) = a match { 
    case Left(i) => i + b 
    case Right(s) => s + b 
} 

Si tiene dos listas muy largas arg que difieren sólo en un arg, puede ser que valga la pena ...

+0

Bueno, traté de usar argumentos por defecto para hacer que mi código sea más conciso y legible ... en realidad agregué una conversión implícita a la clase en un caso que simplemente convertía el tipo alternativo al tipo aceptado. Simplemente se siente feo. ¡Y el enfoque con los argumentos predeterminados debería funcionar! – soc

+0

Debe tener cuidado con tales conversiones, ya que se aplican para todos los usos de 'Oither' y no solo para' foo' - de esta manera, siempre que se solicite un valor 'Oither [A, B]', ambos 'A' y' B' son aceptados. En su lugar, se debe definir un tipo que solo sea aceptado por funciones que tengan argumentos predeterminados (como 'foo' aquí), si se quiere ir en esta dirección; por supuesto, se vuelve aún menos claro si esta es una solución conveniente. – Blaisorblade

56

Sería muy difícil obtener una especificación legible y precisa para las interacciones de resolución de sobrecarga con argumentos predeterminados. Por supuesto, para muchos casos individuales, como el presentado aquí, es fácil decir lo que debería suceder. Pero eso no es suficiente. Necesitaríamos una especificación que decida todos los casos de esquina posibles. La resolución de sobrecarga ya es muy difícil de especificar. Agregar argumentos predeterminados en la mezcla lo haría aún más difícil. Es por eso que hemos optado por separar los dos.

+3

Gracias por su respuesta. Lo que probablemente me confundió fue que básicamente en todas partes el compilador solo se queja si realmente hay alguna ambigüedad. Pero aquí el compilador se queja porque podría haber casos similares en los que pudiera surgir ambigüedad. Entonces, en el primer caso, el compilador solo se queja si hay un problema probado, pero en el segundo caso el comportamiento del compilador es mucho menos preciso y desencadena errores para el código "aparentemente válido". Al ver esto con el principio del menor asombro, esto es un poco desafortunado. – soc

+2

"Sería muy difícil obtener una especificación legible y precisa [...] "significa que existe una posibilidad real de que la situación actual pueda mejorar si alguien intensifica con una buena especificación y/o implementación? La situación actual imho limita un poco la usabilidad de los parámetros con nombre/predeterminados ... – soc

+0

Hay un proceso para proponer cambios a la especificación. http://www.scala-lang.org/node/233 –

0

Según tengo entendido, puede haber colisiones de nombres en las clases compiladas con valores de argumento predeterminados. He visto algo a lo largo de estas líneas mencionado en varios hilos.

La especificación argumento con nombre está aquí: http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf

Afirma:

Overloading If there are multiple overloaded alternatives of a method, at most one is 
allowed to specify default arguments. 

Así que, por el momento, en todo caso, que no va a trabajar.

se podría hacer algo parecido a lo que podría hacer en Java, por ejemplo:

def foo(a: String)(b: Int) = a + (if (b > 0) b else 42) 
1

Uno de los escenarios posibles es


    def foo(a: Int)(b: Int = 10)(c: String = "10") = a + b + c 
    def foo(a: Int)(b: String = "10")(c: Int = 10) = a + b + c 

El compilador será confundido acerca de cuál de ellos para llamar. En la prevención de otros posibles peligros, el compilador permitiría como mucho un método sobrecargado tiene argumentos predeterminados.

Sólo mi conjetura :-)

Cuestiones relacionadas