¿Por qué "evitar la sobrecarga de métodos"?

78

¿Por qué Jorge Ortiz aconseja evitar la sobrecarga de métodos?

desaparecido faktor
fuente
¿Podría tener algo que ver con el hecho de que Scala hereda el borrado de tipos de Java?
Justin Niessner
1
@Justin: ¿Qué tiene que ver el borrado de tipo con esto?
missingfaktor
6
¿Por qué no le pregunta a Jorge Ortiz por qué desaconseja la sobrecarga de métodos?
John
No estoy seguro si es aplicable ya que no conozco la intención original de Jorge, pero: michid.wordpress.com/2008/02/08/…
Justin Niessner
12
bah ... bit.ly/aduyIn : '(
missingfaktor

Respuestas:

108

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

  • No puede (actualmente) usar métodos sobrecargados en objetos de paquete.
  • Los errores de aplicabilidad son más difíciles de diagnosticar para quienes llaman a su API.

ACTUALIZACIÓN 3

  • La resolución de sobrecarga estática puede privar a una API de todo tipo de seguridad:
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...
retrónimo
fuente
2
Actualmente, también hay un error en scalac que se desencadena por sobrecarga en ciertos casos. issues.scala-lang.org/browse/SI-7596 .
cvogt
2
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.
Shelby Moore III
3
Actualmente, las clases de tipos no se pueden utilizar en general debido a la falta de un tipo de unión de primera clase . Sí veo que la actitud en la comunidad de Scala, sin embargo, imaginar su lugar addIntToDouble, addDoubleToIntes 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.
Shelby Moore III
2
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.

Jorge Ortiz
fuente
2
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.

ewernli
fuente
2
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. .
Daniel C. Sobral