Parece que todas as novas linguagens de programação ou pelo menos as que se tornaram populares usam inferência de tipos. Até o Javascript obteve tipos e inferência de tipos através de várias implementações (Acscript, texto datilografado etc). Parece ótimo para mim, mas estou me perguntando se há algum trade-offs ou por que, digamos, Java ou as boas linguagens antigas não têm inferência de tipo
- Ao declarar uma variável em Go sem especificar seu tipo (usando var sem um tipo ou a sintaxe: =), o tipo da variável é deduzido do valor no lado direito.
- D permite escrever grandes fragmentos de código sem especificar tipos redundantes, como fazem as linguagens dinâmicas. Por outro lado, a inferência estática deduz tipos e outras propriedades de código, fornecendo o melhor dos mundos estático e dinâmico.
- O mecanismo de inferência de tipo no Rust é bastante inteligente. Ele faz mais do que olhar para o tipo de valor r durante uma inicialização. Também mostra como a variável é usada posteriormente para inferir seu tipo.
- O Swift usa a inferência de tipo para calcular o tipo apropriado. A inferência de tipo permite que um compilador deduza o tipo de uma expressão específica automaticamente quando compila seu código, simplesmente examinando os valores que você fornece.
programming-languages
type-systems
O usuário sem chapéu
fonte
fonte
var
porque às vezes pode prejudicar a legibilidade.var
e C ++auto
) e inferência de tipo (bidirecional, como Haskelllet
). No primeiro caso, o tipo de um nome pode ser deduzido apenas do inicializador - seus usos devem seguir o tipo do nome. No último caso, o tipo de um nome também pode ser deduzido de seus usos - o que é útil porque você pode escrever simplesmente[]
para uma sequência vazia, independentemente do tipo de elemento, ounewEmptyMVar
para uma nova referência mutável nula, independentemente do referente tipo.Respostas:
O sistema de tipos de Haskell é totalmente inferível (deixando de lado a recursão polimórfica, certas extensões de linguagem e a temida restrição de monomorfismo ), mas os programadores ainda frequentemente fornecem anotações de tipo no código-fonte, mesmo quando não precisam. Por quê?
map :: (a -> b) -> [a] -> [b]
. Sua forma mais geral (fmap :: Functor f => (a -> b) -> f a -> f b
) se aplica a todos osFunctor
s, não apenas às listas. Mas considerou-se quemap
seria mais fácil de entender para iniciantes, por isso vive ao lado de seu irmão maior.No geral, as desvantagens de um sistema estaticamente digitado, mas inferível, são as mesmas que as desvantagens da digitação estática em geral, uma discussão bastante desgastada neste site e em outras (Googling "desvantagens de digitação estática", você terá centenas de páginas de guerras de chamas). Obviamente, algumas dessas desvantagens são melhoradas pela menor quantidade de anotações de tipo em um sistema inferível. Além disso, a inferência de tipo tem suas próprias vantagens: o desenvolvimento acionado por orifício não seria possível sem a inferência de tipo.
O Java * prova que uma linguagem que exige muitas anotações de tipo fica irritante, mas com muito poucas você perde as vantagens que descrevi acima. Os idiomas com inferência do tipo opt-out atingem um equilíbrio agradável entre os dois extremos.
* Mesmo o Java, esse grande bode expiatório, realiza uma certa quantidade de inferência de tipo local . Em uma declaração como
Map<String, Integer> = new HashMap<>();
, você não precisa especificar o tipo genérico do construtor. Por outro lado, as linguagens no estilo ML são tipicamente globalmente inferíveis.fonte
Functor
. Lista emap
é mais provável que seja familiar a Haskellers não experientes.var
um exemplo de inferência de tipo?var
é um exemplo adequado.x
; neste último, não há um tipo a determinar, todos os tipos são conhecidos e você apenas precisa verificar se a expressão faz sentido. A diferença se torna mais importante à medida que você passa por exemplos triviais ex
é usada em vários lugares; o compilador deve então verificar os locais ondex
é usado para determinar 1) é possível atribuirx
um tipo para que o código digite check? 2) Se for, qual é o tipo mais geral que podemos atribuir?new HashMap<>();
sintaxe foi adicionada apenas no Java 7, e as lambdas no Java 8 permitem bastante inferência de tipo "real".Em C #, a inferência de tipo ocorre no tempo de compilação, portanto, o custo de tempo de execução é zero.
Por uma questão de estilo,
var
é usado para situações em que é inconveniente ou desnecessário especificar manualmente o tipo. Linq é uma dessas situações. Outro é:sem o qual você repetiria seu nome de tipo realmente longo (e os parâmetros de tipo) em vez de simplesmente dizer
var
.Use o nome real do tipo ao ser explícito para melhorar a clareza do código.
Existem algumas situações em que a inferência de tipo não pode ser usada, como declarações de variáveis de membros cujos valores são definidos no momento da construção ou onde você realmente deseja que o intellisense funcione corretamente (o IDE de Hackerrank não fará o sentido de inteligência dos membros da variável, a menos que você declare o tipo explicitamente) .
fonte
Boa pergunta!
E há mais linguagens esotéricas que não podem fazer suas estranhezas sem anotação explícita de tipo. Até agora, não há nenhum que eu saiba que seja comum / popular / promissor o suficiente para mencionar, exceto de passagem.
fonte
var foo = x => x;
falha porque a linguagem precisa inferirx
aqui e não tem nada para continuar. Quando as lambdas são construídas, elas são construídas quando delegados de tipo explícitoFunc<int, int> foo = x => x
são construídos no CIL, comoFunc<int, int> foo = new Func<int, int>(x=>x);
onde o lambda é construído como uma função gerada e de tipo explícito. Para mim, inferir o tipo dex
é parte integrante de inferência de tipos ...x => x
estão contidas no próprio lambda - não se referem a nenhuma variável do escopo circundante. Qualquer linguagem funcional sã iria correctamente inferir que seu tipo é "para todos os tiposa
,a -> a
". Eu acho que o DeadMG está tentando dizer é que o sistema de tipos do C # carece da propriedade type principal, o que significa que sempre é possível descobrir o tipo mais geral para qualquer expressão. É uma propriedade muito fácil de destruir.O Java passa a ser a exceção e não a regra aqui. Até o C ++ (que eu acredito qualificar como uma "boa e antiga linguagem" :)) suporta inferência de tipo com a
auto
palavra - chave desde o padrão C ++ 11. Ele não funciona apenas com declaração de variável, mas também como tipo de retorno de função, o que é especialmente útil em algumas funções complexas de modelo.Digitação implícita e inferência de tipo têm muitos casos de uso bons e também existem alguns casos de uso em que você realmente não deve fazê-lo. Às vezes, isso é questão de gosto e também assunto de debate.
Mas, sem dúvida, há casos de uso bons, é por si só uma razão legítima para qualquer linguagem implementá-lo. Também não é um recurso difícil de implementar, sem penalidade no tempo de execução e não afeta o tempo de compilação significativamente.
Não vejo uma desvantagem real em dar uma oportunidade ao desenvolvedor de usar inferência de tipo.
Algumas pessoas que responderam dizem que a digitação explícita é boa às vezes e certamente estão certas. Mas não oferecer suporte à digitação implícita significaria que um idioma impõe a digitação explícita o tempo todo.
Portanto, a verdadeira desvantagem é quando uma linguagem não suporta digitação implícita, porque com isso afirma que não há razão legítima para o desenvolvedor usá-la, o que não é verdade.
fonte
A principal distinção entre um sistema de inferência do tipo Hindley-Milner e inferência do tipo Go é a direção do fluxo de informações. No HM, as informações de tipo fluem para frente e para trás via unificação; no Go, as informações de tipo fluem apenas para frente: calcula apenas substituições para frente.
A inferência de tipo HM é uma inovação esplêndida que funciona bem com linguagens funcionais de tipo polimórfico, mas os autores de Go provavelmente argumentam que ela tenta fazer muito:
O fato de a informação fluir para frente e para trás significa que a inferência do tipo HM é um assunto muito não-local; na ausência de anotações de tipo, todas as linhas de código do seu programa podem estar contribuindo para a digitação de uma única linha de código. Se você tiver apenas substituição direta, você sabe que a fonte de um erro de tipo deve estar no código que precede seu erro; não o código que vem depois.
Com a inferência do tipo HM, você tende a pensar em restrições: ao usar um tipo, você restringe os possíveis tipos que ele pode ter. No final do dia, pode haver algumas variáveis de tipo que são deixadas totalmente sem restrições. O insight da inferência de tipo HM é que, esses tipos realmente não importam e, portanto, são transformados em variáveis polimórficas. No entanto, esse polimorfismo extra pode ser indesejável por várias razões. Primeiro, como algumas pessoas apontaram, esse polimorfismo extra pode ser indesejável: HM concluindo um tipo falso e polimórfico para algum código falso e levar a erros estranhos mais tarde. Segundo, quando um tipo é deixado polimórfico, isso pode ter consequências para o comportamento em tempo de execução. Por exemplo, um resultado intermediário excessivamente polimórfico é a razão pela qual 'mostra. read 'é considerado ambíguo em Haskell; como outro exemplo,
fonte
Dói a legibilidade.
Comparar
vs
É especialmente um problema ao ler fora de um IDE, como no github.
fonte
var
poderá ser usado sem prejudicar a legibilidade.var objects = GetBusinessObjects();