Vejo que já existem várias respostas excelentes. Algumas das quais repetirei, mas às vezes você só quer colocar as coisas com suas próprias palavras. Vou comentar com alguns exemplos do C ++, porque essa é a linguagem com a qual tenho mais familiaridade.
O que é necessário nunca é imprudente. A inferência de tipo é necessária para tornar outros recursos de linguagem práticos. Em C ++, é possível ter tipos indizíveis.
struct {
double x, y;
} p0 = { 0.0, 0.0 };
// there is no name for the type of p0
auto p1 = p0;
O C ++ 11 adicionou lambdas que também são indizíveis.
auto sq = [](int x) {
return x * x;
};
// there is no name for the type of sq
A inferência de tipo também sustenta modelos.
template <class x_t>
auto sq(x_t const& x)
{
return x * x;
}
// x_t is not known until it is inferred from an expression
sq(2); // x_t is int
sq(2.0); // x_t is double
Mas suas perguntas foram "por que eu, o programador, gostaria de inferir o tipo de minhas variáveis quando leio o código? Não é mais rápido para alguém apenas ler o tipo do que pensar que tipo existe?"
A inferência de tipo remove a redundância. Quando se trata de ler código, às vezes pode ser mais rápido e fácil ter informações redundantes no código, mas a redundância pode ofuscar as informações úteis . Por exemplo:
std::vector<int> v;
std::vector<int>::iterator i = v.begin();
Não é preciso muita familiaridade com a biblioteca padrão para um programador de C ++ identificar que eu sou um iterador, i = v.begin()
portanto a declaração de tipo explícita é de valor limitado. Por sua presença, oculta os detalhes mais importantes (como os que i
apontam para o início do vetor). A boa resposta de @amon fornece um exemplo ainda melhor de verbosidade que oculta detalhes importantes. Em contraste, o uso de inferência de tipo dá maior destaque aos detalhes importantes.
std::vector<int> v;
auto i = v.begin();
Embora a leitura do código seja importante, não é suficiente, em algum momento você precisará parar de ler e começar a escrever um novo código. A redundância no código torna a modificação do código mais lenta e mais difícil. Por exemplo, digamos que tenho o seguinte fragmento de código:
std::vector<int> v;
std::vector<int>::iterator i = v.begin();
No caso em que eu preciso alterar o tipo de valor do vetor para alterar o código para:
std::vector<double> v;
std::vector<double>::iterator i = v.begin();
Nesse caso, tenho que modificar o código em dois lugares. Contraste com a inferência de tipo, onde o código original é:
std::vector<int> v;
auto i = v.begin();
E o código modificado:
std::vector<double> v;
auto i = v.begin();
Note que agora só preciso alterar uma linha de código. Extrapole isso para um programa grande e a inferência de tipo pode propagar as alterações para os tipos muito mais rapidamente do que com um editor.
A redundância no código cria a possibilidade de erros. Sempre que seu código depende de duas informações mantidas equivalentes, existe a possibilidade de erro. Por exemplo, há uma inconsistência entre os dois tipos nesta declaração que provavelmente não se destina:
int pi = 3.14159;
A redundância torna a intenção mais difícil de discernir. Em alguns casos, a inferência de tipo pode ser mais fácil de ler e entender, porque é mais simples que a especificação de tipo explícita. Considere o fragmento de código:
int y = sq(x);
No caso que sq(x)
retorna um int
, não é óbvio se y
é um int
porque é o tipo de retorno sq(x)
ou porque é adequado às instruções que são usadas y
. Se eu alterar outro código para que sq(x)
não retorne mais int
, é incerto a partir dessa linha apenas se o tipo de y
deve ser atualizado. Contraste com o mesmo código, mas usando inferência de tipo:
auto y = sq(x);
Neste, a intenção é clara, y
deve ser do mesmo tipo retornada por sq(x)
. Quando o código altera o tipo de retorno sq(x)
, o tipo de y
alteração é correspondido automaticamente.
Em C ++, existe uma segunda razão pela qual o exemplo acima é mais simples com inferência de tipo, a inferência de tipo não pode introduzir conversão implícita de tipo. Se o tipo de retorno de sq(x)
não for int
, o compilador insere silenciosamente uma conversão implícita em int
. Se o tipo de retorno de sq(x)
é um tipo complexo de tipo que define operator int()
, essa chamada de função oculta pode ser arbitrariamente complexa.