programar - ¿Por qué el compilador Scala no permite métodos sobrecargados con argumentos predeterminados?
scala introduccion (7)
Si bien puede haber casos válidos donde tales sobrecargas de métodos podrían volverse ambiguas, ¿por qué el compilador desautoriza el código que no es ambiguo ni en tiempo de compilación ni en tiempo de ejecución?
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 la cual estas restricciones no se puedan aflojar un poco?
Especialmente cuando la conversión de código Java sobrecargado a los argumentos predeterminados de Scala es muy importante y no es agradable averiguar después de reemplazar muchos métodos Java por un método Scala que la especificación / compilador impone restricciones arbitrarias.
Lo que funcionó para mí es redefinir (al estilo Java) los métodos de sobrecarga.
def foo(a: Int, b: Int) = a + b
def foo(a: Int, b: String) = a + b
def foo(a: Int) = a + "42"
def foo(a: String) = a + "42"
Esto asegura al compilador la resolución que desea de acuerdo con los parámetros actuales.
Me gustaría citar a Lukas Rytz (desde here ):
La razón es que queríamos un esquema de nomenclatura determinista para los métodos generados que devuelven los argumentos predeterminados. Si tú escribes
def f(a: Int = 1)
el compilador genera
def f$default$1 = 1
Si tiene dos sobrecargas con valores predeterminados en la misma posición de parámetro, necesitaríamos un esquema de nombres diferente. Pero queremos mantener estable el código de bytes generado en múltiples ejecuciones del compilador.
Una solución para la futura versión de Scala podría ser incorporar nombres de tipo de los argumentos no predeterminados (los que están al principio de un método, que eliminan la ambigüedad de las versiones sobrecargadas) en el esquema de denominación, 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 escribir una propuesta SIP ?
No puedo responder su pregunta, pero aquí hay una solución alternativa:
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 arg muy largas que difieren en una sola arg, podría valer la pena ...
Según entiendo, 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 del argumento nombrado 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.
Entonces, por el momento, de todos modos, no va a funcionar.
Podría hacer algo como lo que podría hacer en Java, por ejemplo:
def foo(a: String)(b: Int) = a + (if (b > 0) b else 42)
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.
Si llamó a foo()
cuál debería llamar?
Uno de los posibles escenarios 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 estará confundido acerca de cuál llamar. En la prevención de otros posibles peligros, el compilador permitiría como mucho un método sobrecargado tiene argumentos predeterminados.
Solo mi conjetura :-)