Estou desenvolvendo uma linguagem compilada de forma estática e fortemente tipada e estou revisitando a ideia de incluir sobrecarga de função como um recurso de linguagem. Percebi que sou um pouco tendencioso, vindo principalmente de um C[++|#]
plano de fundo.
Quais são as mais convincentes argumentos para e contra , incluindo a função de sobrecarga em um idioma?
EDIT: Não há ninguém que tenha uma opinião contrária?
Bertrand Meyer (criador da Eiffel em 1985/1986) chama o método de sobrecarregar isso: (fonte)
um mecanismo de vaidade que não traz nada ao poder semântico de uma linguagem OO, mas dificulta a legibilidade e complica a tarefa de todos
Agora, essas são algumas generalizações abrangentes, mas ele é um cara inteligente, então acho que é seguro dizer que ele poderia apoiá-las, se necessário. Na verdade, ele quase convencera Brad Abrams (um dos desenvolvedores do CLSv1) de que o .NET não deveria suportar a sobrecarga de métodos. (fonte) Isso é algo poderoso. Alguém pode lançar alguma luz sobre seus pensamentos e se seu ponto de vista ainda é justificado 25 anos depois?
fonte
Eu recomendo pelo menos estar ciente das classes de tipo no Haskell. As classes de tipo foram criadas para serem uma abordagem disciplinada à sobrecarga de operadores, mas descobriram outros usos e, até certo ponto, fizeram Haskell o que é.
Por exemplo, aqui está um exemplo de sobrecarga ad-hoc (Haskell não muito válido):
E aqui está o mesmo exemplo de sobrecarga com classes de tipo:
A desvantagem desta situação é que você tem que vir acima com nomes funky para todos os seus typeclasses (como em Haskell, você tem o bastante abstrato
Monad
,Functor
,Applicative
bem como o mais simples e mais reconhecívelEq
,Num
eOrd
).Uma vantagem é que, quando você estiver familiarizado com uma classe, você sabe como usar qualquer tipo nessa classe. Além disso, é fácil proteger funções de tipos que não implementam as classes necessárias, como em:
Edit: No Haskell, se você quiser um
==
operador que aceite dois tipos diferentes, poderá usar uma classe de tipo com vários parâmetros:Obviamente, essa é provavelmente uma má idéia, pois permite explicitamente comparar maçãs e laranjas. No entanto, convém considerar isso
+
, pois adicionar umWord8
aInt
realmente é uma coisa sensata a ser feita em alguns contextos.fonte
(==) :: Int -> Float -> Bool
qualquer lugar? (independentemente de ser uma boa ideia, é claro)class Eq a ...
seria traduzido para a pseudo-família Cinterface Eq<A> {bool operator==(A x, A y);}
e, em vez de usar código modelado para comparar objetos arbitrários, você usa essa 'interface'. Isso está certo?==
estar em um espaço de nome diferente, mas não permite substituí-lo. Observe que há um único espaço para nome (Prelude
) incluído por padrão, mas você pode impedir o carregamento usando extensões ou importando-o explicitamente (import Prelude ()
não importará nadaPrelude
eimport qualified Prelude as P
não inserirá símbolos no espaço para nome atual).Permitir sobrecarga de função, você não pode fazer o seguinte com parâmetros opcionais (ou, se puder, não muito bem).
exemplo trivial não assume nenhum
base.ToString()
métodofonte
Eu sempre preferi parâmetros padrão em vez de sobrecarga de função. Funções sobrecarregadas geralmente chamam uma versão "padrão" com parâmetros padrão. Por que escrever
Quando eu poderia fazer:
Dito isso, percebo que, às vezes, funções sobrecarregadas fazem coisas diferentes, em vez de apenas chamar outra variante com parâmetros padrão ... mas, nesse caso, não é uma má idéia (de fato, provavelmente é uma boa idéia) apenas dar uma nome diferente.
(Além disso, os argumentos de palavra-chave no estilo Python funcionam muito bem com os parâmetros padrão.)
fonte
Array Slice(int start, int length) {...}
sobrecarregadoArray Slice(int start) {return this.Slice(start, this.Count - start);}
? Isso não pode ser codificado usando parâmetros padrão. Você acha que eles deveriam receber nomes diferentes? Se sim, como você os nomearia?indexOf(char ch)
+indexOf(Date dt)
em uma lista. Também gosto de valores padrão, mas eles não são intercambiáveis com a digitação estática.Você acabou de descrever o Java. Ou c #.
Por que você está reinventando a roda?
Certifique-se de que o tipo de retorno faça parte da assinatura do método esobrecarregue o conteúdo do seu coração; ele realmente limpa o código quando você não precisa dizer.fonte
EnumX.Flag1 | Flag2 | Flag3
. Eu não vou implementar isso, no entanto. Se eu fiz e o tipo de retorno não foi usado, procuraria um tipo de retorno devoid
.Grrr .. não é privilégio suficiente para comentar ainda ..
@ Wheeler Mason: Esteja ciente do Ada, que sobrecarrega no tipo de retorno. Também minha linguagem Felix faz isso em alguns contextos, especificamente, quando uma função retorna outra função e há uma chamada como:
o tipo de b pode ser usado para resolução de sobrecarga. Também o C ++ sobrecarrega no tipo de retorno em algumas circunstâncias:
De fato, existem algoritmos para sobrecarga no tipo de retorno, usando inferência de tipo. Na verdade, não é tão difícil de fazer com uma máquina, o problema é que os humanos acham difícil. (Eu acho que o esboço é dado no Dragon Book, o algoritmo é chamado de algoritmo de gangorra, se bem me lembro)
fonte
Caso de uso contra a implementação de sobrecarga de função: 25 métodos com nomes semelhantes que meio que fazem as mesmas coisas, mas com conjuntos de argumentos completamente diferentes em uma ampla variedade de padrões.
Caso de uso contra a não sobrecarga de funções de implementação: 5 métodos com nomes semelhantes com conjuntos de tipos muito semelhantes no mesmo padrão.
No final do dia, não estou ansioso para ler os documentos de uma API produzida em ambos os casos.
Mas, em um caso, é sobre o que os usuários podem fazer. No outro caso, é o que os usuários devem fazer devido a uma restrição de idioma. Na IMO, é melhor pelo menos permitir a possibilidade de os autores do programa serem inteligentes o suficiente para sobrecarregar sensatamente sem criar ambiguidade. Quando você dá um tapa em suas mãos e tira a opção, basicamente garante que a ambiguidade acontecerá. Estou mais confiando no usuário a fazer a coisa certa do que supor que ele sempre fará a coisa errada. Na minha experiência, o protecionismo tende a levar a comportamentos ainda piores por parte da comunidade de uma língua.
fonte
Eu escolhi fornecer sobrecarga comum e classes de tipo multi-tipo no meu idioma Felix.
Considero a sobrecarga (aberta) essencial, especialmente em um idioma que possui muitos tipos numéricos (Felix possui todos os tipos numéricos de C). No entanto, diferentemente do C ++, que abusa da sobrecarga ao fazer com que os modelos dependam dele, o polimorfismo Felix é paramétrico: você precisa sobrecarregar os modelos em C ++ porque os modelos em C ++ são mal projetados.
As classes de tipo também são fornecidas no Felix. Para aqueles que conhecem C ++, mas não criticam Haskell, ignore aqueles que o descrevem como sobrecarga. Não é remotamente como sobrecarregar, é como especialização de modelo: você declara um modelo que não implementa e fornece implementações para casos específicos, conforme necessário. A digitação é parametricamente polimórfica, a implementação é por instanciação ad hoc, mas não se destina a ser irrestrita: ela precisa implementar a semântica pretendida.
No Haskell (e C ++), você não pode declarar a semântica. Em C ++, a idéia "Conceitos" é aproximadamente uma tentativa de aproximar a semântica. Em Felix, você pode aproximar a intenção com axiomas, reduções, lemas e teoremas.
A principal e única vantagem da sobrecarga (aberta) em uma linguagem bem baseada em princípios como o Felix é que facilita a lembrança dos nomes das funções da biblioteca, tanto para o criador do programa quanto para o revisor de código.
A principal desvantagem da sobrecarga é o algoritmo complexo necessário para implementá-lo. Também não se encaixa muito bem com a inferência de tipo: embora os dois não sejam totalmente exclusivos, o algoritmo para fazer as duas coisas é complexo o suficiente, o programador provavelmente não seria capaz de prever os resultados.
Em C ++, isso também é um problema, pois possui um algoritmo de correspondência desleixada e também suporta conversões de tipo automáticas: no Felix, eu "consertei" esse problema exigindo uma correspondência exata e nenhuma conversão de tipos automática.
Então você tem uma escolha: sobrecarregar ou digitar inferência. A inferência é atraente, mas também é muito difícil de implementar de uma maneira que diagnostica adequadamente os conflitos. Ocaml, por exemplo, informa onde detecta um conflito, mas não de onde inferiu o tipo esperado.
A sobrecarga não é muito melhor, mesmo se você tiver um compilador de qualidade que tente informar todos os candidatos, pode ser difícil ler se os candidatos são polimórficos e, pior ainda, se for hackeamento de modelo C ++.
fonte
Tudo se resume ao contexto, mas acho que sobrecarregar torna uma classe muito mais útil quando estou usando uma escrita por outra pessoa. Você geralmente acaba com menos redundância.
fonte
Se você deseja ter usuários familiarizados com os idiomas da família C, sim, deve fazê-lo, porque os usuários o esperam.
fonte