No conozco todos los lenguajes de programación, pero está claro que generalmente no se admite la posibilidad de sobrecargar un método teniendo en cuenta su tipo de retorno (suponiendo que sus argumentos sean del mismo número y tipo).
Me refiero a algo como esto:
int method1 (int num)
{
}
long method1 (int num)
{
}
No es que sea un gran problema para la programación, pero en algunas ocasiones me hubiera gustado.
Obviamente, no habría forma de que esos lenguajes admitieran eso sin una forma de diferenciar qué método se llama, pero la sintaxis para eso puede ser tan simple como algo como [int] method1 (num) o [long] method1 (num) de esa manera el compilador sabría cuál sería el que se llamaría.
No sé cómo funcionan los compiladores, pero eso no parece ser tan difícil de hacer, así que me pregunto por qué algo así no suele implementarse.
¿Cuáles son las razones por las que algo así no es compatible?
fuente
Foo
yBar
.Respuestas:
Complica la verificación de tipo.
Cuando solo permite la sobrecarga basada en tipos de argumento, y solo permite deducir tipos variables de sus inicializadores, toda su información de tipo fluye en una dirección: hacia arriba en el árbol de sintaxis.
Cuando permite que la información de tipo viaje en ambas direcciones, como deducir el tipo de una variable de su uso, necesita un solucionador de restricciones (como el Algoritmo W, para sistemas de tipo Hindley-Milner) para determinar el tipo.
Aquí, teníamos que dejar el tipo de
x
como una variable de tipo sin resolver∃T
, donde todo lo que sabemos al respecto es que es analizable. Solo más tarde, cuandox
se usa en un tipo concreto, tenemos suficiente información para resolver la restricción y determinar eso∃T = int
, lo que propaga la información del tipo en el árbol de sintaxis desde la expresión de llamada haciax
.Si no pudiéramos determinar el tipo de
x
, este código también se sobrecargaría (por lo que la persona que llama determinaría el tipo) o tendríamos que informar un error sobre la ambigüedad.A partir de esto, un diseñador de idiomas puede concluir:
Agrega complejidad a la implementación.
Hace que la verificación de tipos sea más lenta, en casos patológicos, exponencialmente.
Es más difícil producir buenos mensajes de error.
Es muy diferente del status quo.
No tengo ganas de implementarlo.
fuente
Porque es ambiguo. Usando C # como ejemplo:
¿Qué sobrecarga debemos usar?
Ok, tal vez eso fue un poco injusto. El compilador no puede determinar qué tipo usar si no lo decimos en su lenguaje hipotético. Por lo tanto, la escritura implícita es imposible en su idioma y hay métodos anónimos y Linq junto con él ...
¿Que tal este? (Firmas ligeramente redefinidas para ilustrar el punto).
¿Deberíamos usar la
int
sobrecarga o lashort
sobrecarga? Simplemente no lo sabemos, tendremos que especificarlo con su[int] method1(num)
sintaxis. Lo cual es un poco difícil de analizar y escribir para ser honesto.La cuestión es que es una sintaxis sorprendentemente similar a un método genérico en C #.
(C ++ y Java tienen características similares).
En resumen, los diseñadores de idiomas eligieron resolver el problema de una manera diferente para simplificar el análisis y habilitar funciones de lenguaje mucho más potentes.
Dices que no sabes mucho sobre compiladores. Recomiendo aprender sobre gramáticas y analizadores. Una vez que comprenda qué es una gramática libre de contexto, tendrá una idea mucho mejor de por qué la ambigüedad es algo malo.
fuente
short
yint
.method
devuelve un short o int, y el tipo se definió como long.long
/int
/short
tiene más que ver con las complejidades del subtipo y / o las conversiones implícitas que con la sobrecarga del tipo de retorno. Después de todo, los literales numéricos están sobrecargados en su tipo de retorno en C ++, Java, C♯ y muchos otros, y eso no parece presentar un problema. Simplemente puede inventar una regla: por ejemplo, elija el tipo más específico / general.Todas las características del lenguaje agregan complejidad, por lo que tienen que proporcionar un beneficio suficiente para justificar las trampas inevitables, los casos de esquina y la confusión del usuario que crea cada característica. Para la mayoría de los idiomas, este simplemente no proporciona suficientes beneficios para justificarlo.
En la mayoría de los idiomas, es de esperar que la expresión
method1(2)
tenga un tipo definido y un valor de retorno más o menos predecible. Pero si permite la sobrecarga en los valores de retorno, eso significa que es imposible saber qué significa esa expresión en general sin tener en cuenta el contexto que la rodea. Considere lo que sucede cuando tiene ununsigned long long foo()
método cuya implementación termina conreturn method1(2)
? ¿Debería eso llamar lalong
sobrecarga de retorno o laint
sobrecarga de retorno o simplemente dar un error del compilador?Además, si tiene que ayudar al compilador anotando el tipo de retorno, no solo está inventando más sintaxis (lo que aumenta todos los costos antes mencionados para permitir que la característica exista), sino que efectivamente está haciendo lo mismo que crear dos métodos con nombres diferentes en un lenguaje "normal". ¿Es
[long] method1(2)
más intuitivo quelong_method1(2)
?Por otro lado, algunos lenguajes funcionales como Haskell con sistemas de tipos estáticos muy fuertes permiten este tipo de comportamiento, porque su inferencia de tipos es lo suficientemente potente como para que rara vez necesite anotar el tipo de retorno en esos idiomas. Pero eso solo es posible porque esos idiomas realmente imponen la seguridad de los tipos, más que cualquier lenguaje tradicional, además de requerir que todas las funciones sean puras y referencialmente transparentes. No es algo que sea factible en la mayoría de los idiomas de OOP.
fuente
Que está disponible en Swift y funciona bien allí. Obviamente, no puede tener un tipo ambiguo en ambos lados, por lo que debe ser conocido a la izquierda.
Usé esto en una simple API de codificación / decodificación .
Eso significa que las llamadas donde se conocen tipos de parámetros, como la
init
de un objeto, funcionan de manera muy simple:Si presta mucha atención, notará la
dechOpt
llamada anterior. Descubrí por las malas que sobrecargar el mismo nombre de función donde el diferenciador estaba devolviendo un opcional era demasiado propenso a errores ya que el contexto de la llamada podría introducir una expectativa de que era opcional.fuente
fuente
var1
y proceder con la inferencia de tipo y tan pronto como alguna otra expresión determine el tipo devar1
uso que se utiliza para seleccionar el implementación correcta En la línea inferior, mostrar un caso en el que la inferencia de tipos no es trivial rara vez prueba un punto distinto de la inferencia de tipos que generalmente no es trivial.method1
debe declararse para devolver un tipo particular.