Eu nunca entendi declarações como esta. Para ser sincero, mesmo se você declarar o tipo de retorno de uma função, poderá e irá esquecê-lo depois de escrever muitas linhas de código e ainda precisará retornar à linha na qual é declarada usando a função de pesquisa de seu editor de texto para verificá-lo.
Não se trata de você esquecer o tipo de retorno - isso sempre vai acontecer. É sobre a ferramenta poder informar que você esqueceu o tipo de retorno.
Além disso, como as funções são declaradas com o tipo funcname()...
, sem o tipo de conhecimento, você terá que pesquisar sobre cada linha na qual a função é chamada, porque você só sabe funcname
, enquanto no Python e similares você poderia procurar def funcname
ou o function funcname
que só acontece uma vez , na declaração.
Esta é uma questão de sintaxe, que não tem nenhuma relação com a digitação estática.
A sintaxe da família C é realmente hostil quando você deseja procurar uma declaração sem ter ferramentas especializadas à sua disposição. Outros idiomas não têm esse problema. Veja a sintaxe da declaração de Rust:
fn funcname(a: i32) -> i32
Além disso, com REPLs, é trivial testar uma função para seu tipo de retorno com entradas diferentes, enquanto que com linguagens de tipo estaticamente você precisará adicionar algumas linhas de código e recompilar tudo apenas para saber o tipo declarado.
Qualquer idioma pode ser interpretado e qualquer idioma pode ter um REPL.
Portanto, além de conhecer o tipo de retorno de uma função que claramente não é um ponto forte das linguagens estaticamente tipadas, como a tipagem estática é realmente útil em projetos maiores?
Eu responderei de uma maneira abstrata.
Um programa consiste em várias operações e essas operações são definidas da maneira que são devido a algumas suposições feitas pelo desenvolvedor.
Algumas suposições estão implícitas e outras são explícitas. Algumas suposições dizem respeito a uma operação próxima a elas, outras dizem respeito a uma operação fora delas. Uma suposição é mais fácil de identificar quando é expressa explicitamente e o mais próximo possível dos locais onde seu valor de verdade é importante.
Um bug é a manifestação de uma suposição que existe no programa, mas não é válida em alguns casos. Para rastrear um bug, precisamos identificar a suposição errônea. Para remover o bug, precisamos remover essa suposição do programa ou alterar algo para que a suposição realmente se mantenha.
Eu gostaria de categorizar suposições em dois tipos.
O primeiro tipo são as suposições que podem ou não ser mantidas, dependendo das entradas do programa. Para identificar uma suposição errônea desse tipo, precisamos procurar no espaço todas as entradas possíveis do programa. Usando suposições educadas e pensamento racional, podemos diminuir o problema e procurar em um espaço muito menor. Mas, ainda assim, à medida que um programa cresce um pouco, seu espaço de entrada inicial cresce a uma taxa enorme - até o ponto em que pode ser considerado infinito para todos os fins práticos.
O segundo tipo são as suposições que definitivamente valem para todas as entradas, ou são definitivamente erradas para todas as entradas. Quando identificamos uma suposição desse tipo como errônea, nem precisamos executar o programa ou testar nenhuma entrada. Quando identificamos uma suposição desse tipo como correta, temos menos um suspeito para nos preocupar quando rastreamos um bug ( qualquer bug). Portanto, há valor em ter o maior número possível de suposições pertencer a esse tipo.
Para colocar uma suposição na segunda categoria (sempre verdadeira ou sempre falsa, independente das entradas), precisamos de uma quantidade mínima de informações para estar disponível no local em que a suposição é feita. No código-fonte de um programa, as informações ficam obsoletas rapidamente (por exemplo, muitos compiladores não fazem análises interprocedurais, o que torna qualquer chamada um limite rígido para a maioria das informações). Precisamos de uma maneira de manter as informações necessárias atualizadas (válidas e próximas).
Uma maneira é ter a fonte dessas informações o mais próximo possível do local onde elas serão consumidas, mas isso pode ser impraticável para a maioria dos casos de uso. Outra maneira é repetir as informações com frequência, renovando sua relevância no código-fonte.
Como você já pode imaginar, os tipos estáticos são exatamente isso - faróis de informações de tipo espalhados pelo código-fonte. Essas informações podem ser usadas para colocar a maioria das suposições sobre correção de tipo na segunda categoria, o que significa que quase qualquer operação pode ser classificada como sempre correta ou sempre incorreta com relação à compatibilidade de tipo.
Quando nossos tipos estão incorretos, a análise economiza tempo, chamando a atenção do erro mais cedo do que tarde. Quando nossos tipos estão corretos, a análise economiza tempo, garantindo que, quando ocorrer um erro, possamos excluir imediatamente erros de tipo.