Eu não conheço todas as linguagens de programação, mas é claro que geralmente a possibilidade de sobrecarregar um método levando em consideração seu tipo de retorno (supondo que seus argumentos sejam o mesmo número e tipo) não é suportada.
Quero dizer algo como isto:
int method1 (int num)
{
}
long method1 (int num)
{
}
Não é que seja um grande problema de programação, mas em algumas ocasiões eu gostaria de recebê-lo.
Obviamente, não haveria como essas linguagens suportarem isso, sem uma maneira de diferenciar qual método está sendo chamado, mas a sintaxe para isso pode ser tão simples quanto algo como [int] method1 (num) ou [long] method1 (num) dessa forma, o compilador saberia qual seria o que seria chamado.
Não sei como os compiladores funcionam, mas isso não parece tão difícil de fazer, então me pergunto por que algo assim geralmente não é implementado.
Quais são as razões pelas quais algo assim não é suportado?
fonte
Foo
eBar
.Respostas:
Isso complica a verificação de tipos.
Quando você só permite sobrecarga com base nos tipos de argumento e deduz apenas tipos de variáveis de seus inicializadores, todas as suas informações de tipo fluem em uma direção: na árvore de sintaxe.
Quando você permite que as informações de tipo viajem em ambas as direções, como deduzir o tipo de uma variável de seu uso, você precisa de um solucionador de restrições (como o Algoritmo W, para sistemas do tipo Hindley-Milner) para determinar o tipo.
Aqui, precisamos deixar o tipo
x
como uma variável de tipo não resolvida∃T
, onde tudo o que sabemos sobre isso é que é analisável. Somente mais tarde, quandox
usado em um tipo concreto, temos informações suficientes para resolver a restrição e determinar isso∃T = int
, que propaga as informações de tipo na árvore da sintaxe da expressão de chamada parax
.Se não formos capazes de determinar o tipo de
x
, esse código também será sobrecarregado (para que o responsável pela chamada determine o tipo) ou teremos que reportar um erro sobre a ambiguidade.A partir disso, um designer de linguagem pode concluir:
Acrescenta complexidade à implementação.
Isso torna a digitação mais lenta - em casos patológicos, exponencialmente.
É mais difícil produzir boas mensagens de erro.
É muito diferente do status quo.
Não tenho vontade de implementá-lo.
fonte
Porque é ambíguo. Usando C # como um exemplo:
Qual sobrecarga devemos usar?
Ok, talvez isso tenha sido um pouco injusto. O compilador não pode descobrir que tipo usar se não o contarmos na sua linguagem hipotética. Portanto, a digitação implícita é impossível no seu idioma e existem métodos anônimos e o Linq junto com ele ...
Que tal este? (Assinaturas ligeiramente redefinidas para ilustrar o ponto.)
Devemos usar a
int
sobrecarga ou ashort
sobrecarga? Simplesmente não sabemos, teremos que especificá-lo com sua[int] method1(num)
sintaxe. O que é um pouco trabalhoso analisar e escrever para ser honesto.O fato é que é uma sintaxe surpreendentemente semelhante a um método genérico em C #.
(C ++ e Java têm recursos semelhantes.)
Em resumo, os designers de idiomas optaram por resolver o problema de uma maneira diferente, a fim de simplificar a análise e ativar recursos de linguagem muito mais poderosos.
Você diz que não sabe muito sobre compiladores. Eu recomendo aprender sobre gramáticas e analisadores. Depois de entender o que é uma gramática livre de contexto, você terá uma idéia muito melhor sobre por que a ambiguidade é uma coisa ruim.
fonte
short
eint
.method
retornasse um short ou int e o tipo fosse definido como um long.long
/int
/short
é mais sobre as complexidades de subtipagem e / ou conversões implícitas do que sobre a sobrecarga do tipo de retorno. Afinal, literais numéricos estão sobrecarregados em seu tipo de retorno em C ++, Java, C♯ e muitos outros, e isso não parece apresentar um problema. Você pode simplesmente criar uma regra: por exemplo, escolha o tipo mais específico / geral.Todos os recursos de idioma adicionam complexidade, portanto, eles precisam fornecer benefícios suficientes para justificar as inevitáveis dicas, casos de canto e confusão do usuário que todo recurso cria. Para a maioria dos idiomas, este simplesmente não fornece benefícios suficientes para justificá-lo.
Na maioria dos idiomas, você esperaria que a expressão
method1(2)
tivesse um tipo definido e um valor de retorno mais ou menos previsível. Mas se você permitir sobrecarregar os valores de retorno, isso significa que é impossível dizer o que essa expressão significa em geral, sem considerar o contexto ao seu redor. Considere o que acontece quando você tem umunsigned long long foo()
método cuja implementação terminareturn method1(2)
? Isso deve chamar along
sobrecarga de retorno ou aint
sobrecarga de retorno ou simplesmente fornecer um erro do compilador?Além disso, se você precisar ajudar o compilador anotando o tipo de retorno, não apenas estará inventando mais sintaxe (o que aumenta todos os custos acima mencionados de permitir que o recurso exista), mas também fazendo efetivamente o mesmo que criando dois métodos de nomes diferentes em uma linguagem "normal". É
[long] method1(2)
mais intuitivo do quelong_method1(2)
?Por outro lado, algumas linguagens funcionais como Haskell, com sistemas de tipos estáticos muito fortes, permitem esse tipo de comportamento, porque sua inferência de tipo é poderosa o suficiente para que você raramente precise anotar o tipo de retorno nesses idiomas. Mas isso só é possível porque esses idiomas realmente reforçam a segurança de tipos, mais do que qualquer idioma tradicional, além de exigir que todas as funções sejam puras e transparente em termos de referência. Não é algo que jamais seria viável na maioria dos idiomas OOP.
fonte
Ele é disponível em Swift e funciona muito bem lá. Obviamente, você não pode ter um tipo ambíguo em ambos os lados, por isso deve ser conhecido à esquerda.
Eu usei isso em uma API simples de codificação / decodificação .
Isso significa que as chamadas onde os tipos de parâmetros são conhecidos, como o
init
de um objeto, funcionam de maneira muito simples:Se você prestar muita atenção, notará a
dechOpt
ligação acima. Descobri da maneira mais difícil que sobrecarregar o mesmo nome de função em que o diferenciador estava retornando um opcional era muito propenso a erros, pois o contexto de chamada poderia introduzir uma expectativa de que era opcional.fonte
fonte
var1
e continuar com a inferência de tipo e assim que alguma outra expressão determinar o tipo devar1
uso usado para selecionar implementação correta. No final das contas, mostrar um caso em que a inferência de tipo não é trivial raramente prova um ponto diferente dessa inferência de tipo geralmente não é trivial.method1
deve ser declarado para retornar um tipo específico.