La resolución de sobrecarga estática puede privar a una API de todo tipo de seguridad:
scala> objectO{ defapply[T](ts: T*) = (); defapply(f: (String => Int)) = () }
defined objectO
scala> O((i: String) => f(i)) // oops, I meant to call the second overload but someone changed the return type of `f` when I wasn't looking...
Los dos primeros problemas no afectan a todos los usos válidos de la sobrecarga. Presentó un informe de error para el tercer número. La restricción a los incumplimientos es por elección y, en teoría, podría arreglarse. La culpa del _.fooproblema es la inferencia de tipo limitada de Scala, no la sobrecarga. Responde a la pregunta, pero algunas de las razones se deben a otras debilidades en Scala que podrían mejorarse. La sobrecarga es más eficiente que el tiempo de ejecución rebajando una disyunción, o que un producto cartesiano de nombres es ruidoso y se desconecta de una semántica compartida.
Escribí en el hilo de discusión (para el informe de error que mencioné en el comentario anterior), "Lo que es malo IMO es esperar que la sobrecarga sea lo que no es, o disminuir la importancia de tener un nombre para una semántica común".
Shelby Moore III
2
La respuesta y los comentarios me parecen muy ingenuos y no mencionan la razón más importante para usar la sobrecarga: cuando un método debe realizar una lógica interna muy diferente según los tipos que se le pasan. La respuesta de "uso implícito" falla inmediatamente, porque puede que no exista ninguna conversión posible de un objeto a un tipo diferente que encarne la lógica especializada.
ely
8
Las razones que dan Gilad y Jason (retronym) son muy buenas razones para evitar la sobrecarga si es posible. Las razones de Gilad se centran en por qué la sobrecarga es problemática en general, mientras que las razones de Jason se centran en por qué es problemática en el contexto de otras características de Scala.
A la lista de Jason, agregaría que la sobrecarga interactúa mal con la inferencia de tipos. Considerar:
val x = ...
foo(x)
Un cambio en el tipo inferido de xpodría alterar el foométodo que se llama. El valor de xno tiene por qué cambiar, solo el tipo inferido de x, lo que podría suceder por todo tipo de razones.
Por todas las razones dadas (y algunas más, estoy seguro de que las olvido), creo que la sobrecarga de métodos debe usarse con la menor moderación posible.
Si no quiere que eso suceda, declare el tipo de x. Si no lo declara, entonces está diciendo que desea que cambie. La semántica de foodebería ser la misma para cada sobrecarga con el mismo número de parámetros, de lo contrario, se diseñó incorrectamente. En cuanto a limitar el alcance de la extraña cascada de cambios de inferencia, los métodos públicos siempre deben declarar sus tipos de retorno. Creo que este fue uno de los problemas que afectaron la compatibilidad binaria de Scala entre versiones.
Shelby Moore III
1
Creo que el consejo no está destinado especialmente a scala, sino a OO en general (hasta ahora sé que se supone que scala es el mejor de su clase entre OO y funcional).
La anulación está bien, es el corazón del polimorfismo y es fundamental para el diseño OO.
La sobrecarga, por otro lado, es más problemática. Con la sobrecarga de métodos, es difícil discernir qué método se invocará realmente y, de hecho, con frecuencia es una fuente de confusión. Rara vez existe una justificación de por qué la sobrecarga es realmente necesaria. La mayoría de las veces, el problema se puede resolver de otra manera y estoy de acuerdo en que la sobrecarga es un olor.
Aquí hay un artículo que explica muy bien lo que quiero decir con "la sobrecarga es una fuente de confusión", que creo que es la razón principal por la que se desaconseja. Es para Java, pero creo que también se aplica a Scala.
Y Scala no es principalmente un lenguaje OO de todos modos.
Daniel Earwicker
4
@ewenli: Jorge es bien conocido en la comunidad de scala y el enlace que proporcionó Rahul fue uno de los consejos de scala de Jorge, pero su respuesta no tiene nada que ofrecer sobre por qué la sobrecarga es mala específicamente para scala , que era claramente la intención de la pregunta. Además, no tengo idea de por qué decidió que la pregunta era confusa de alguna manera; simplemente debe eliminar esto de su respuesta, ya que es totalmente injustificado. -1
oxbow_lakes
6
@Daniel Scala es principalmente un lenguaje OO. ¿Alguna razón por la que no lo pensaría?
Daniel C. Sobral
3
¡Oh, el creador de un lenguaje funcional competitivo no cree que Scala sea lo suficientemente funcional! ¡Gran impacto! :)
Daniel Earwicker
3
@Daniel Scala puede ser un paradigma múltiple, pero sigue siendo principalmente un lenguaje orientado a objetos. Las características funcionales de Scala se implementan como características orientadas a objetos. Incluso el programa Scala más funcional estará compuesto únicamente por objetos y sus respectivas clases y rasgos. Ahora, Martin Odersky puede decir lo que quiera sobre su lenguaje (es su lenguaje, después de todo), pero, en una evaluación estrictamente técnica, Scala está principalmente orientado a objetos, donde por "principalmente" quiero decir que todo lo demás se basa en esta característica. .
Respuestas:
La sobrecarga hace que sea un poco más difícil elevar un método a una función:
object A { def foo(a: Int) = 0 def foo(b: Boolean) = 0 def foo(a: Int, b: Int) = 0 val function = foo _ // fails, must use = foo(_, _) or (a: Int) => foo(a) }
No puede importar selectivamente uno de un conjunto de métodos sobrecargados.
Existe una mayor probabilidad de que surja ambigüedad al intentar aplicar vistas implícitas para adaptar los argumentos a los tipos de parámetros:
scala> implicit def S2B(s: String) = !s.isEmpty S2B: (s: String)Boolean scala> implicit def S2I(s: String) = s.length S2I: (s: String)Int scala> object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") } <console>:15: error: ambiguous reference to overloaded definition, both method foo in object test of type (b: Boolean)Int and method foo in object test of type (a: Int)Int match argument types (java.lang.String) object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") }
Puede inutilizar silenciosamente los parámetros predeterminados:
object test { def foo(a: Int) = 0; def foo(a: Int, b: Int = 0) = 1 }
Individualmente, estas razones no lo obligan a evitar por completo la sobrecarga. Siento que me estoy perdiendo algunos problemas mayores.
ACTUALIZAR
La evidencia se está acumulando.
ACTUALIZACIÓN 2
ACTUALIZACIÓN 3
scala> object O { def apply[T](ts: T*) = (); def apply(f: (String => Int)) = () } defined object O scala> O((i: String) => f(i)) // oops, I meant to call the second overload but someone changed the return type of `f` when I wasn't looking...
fuente
_.foo
problema es la inferencia de tipo limitada de Scala, no la sobrecarga. Responde a la pregunta, pero algunas de las razones se deben a otras debilidades en Scala que podrían mejorarse. La sobrecarga es más eficiente que el tiempo de ejecución rebajando una disyunción, o que un producto cartesiano de nombres es ruidoso y se desconecta de una semántica compartida.addIntToDouble
,addDoubleToInt
es decir, un producto cartesiano de nombres en lugar de tipos estáticos para cada semántica común. Reemplazar la mecanografía con la denominación parece ser regresivo. Java hizo más cosas correctas de las que quizás reconocemos.Las razones que dan Gilad y Jason (retronym) son muy buenas razones para evitar la sobrecarga si es posible. Las razones de Gilad se centran en por qué la sobrecarga es problemática en general, mientras que las razones de Jason se centran en por qué es problemática en el contexto de otras características de Scala.
A la lista de Jason, agregaría que la sobrecarga interactúa mal con la inferencia de tipos. Considerar:
val x = ... foo(x)
Un cambio en el tipo inferido de
x
podría alterar elfoo
método que se llama. El valor dex
no tiene por qué cambiar, solo el tipo inferido dex
, lo que podría suceder por todo tipo de razones.Por todas las razones dadas (y algunas más, estoy seguro de que las olvido), creo que la sobrecarga de métodos debe usarse con la menor moderación posible.
fuente
foo
debería ser la misma para cada sobrecarga con el mismo número de parámetros, de lo contrario, se diseñó incorrectamente. En cuanto a limitar el alcance de la extraña cascada de cambios de inferencia, los métodos públicos siempre deben declarar sus tipos de retorno. Creo que este fue uno de los problemas que afectaron la compatibilidad binaria de Scala entre versiones.Creo que el consejo no está destinado especialmente a scala, sino a OO en general (hasta ahora sé que se supone que scala es el mejor de su clase entre OO y funcional).
La anulación está bien, es el corazón del polimorfismo y es fundamental para el diseño OO.
La sobrecarga, por otro lado, es más problemática. Con la sobrecarga de métodos, es difícil discernir qué método se invocará realmente y, de hecho, con frecuencia es una fuente de confusión. Rara vez existe una justificación de por qué la sobrecarga es realmente necesaria. La mayoría de las veces, el problema se puede resolver de otra manera y estoy de acuerdo en que la sobrecarga es un olor.
Aquí hay un artículo que explica muy bien lo que quiero decir con "la sobrecarga es una fuente de confusión", que creo que es la razón principal por la que se desaconseja. Es para Java, pero creo que también se aplica a Scala.
fuente