Nunca entendí declaraciones como esta. Para ser honesto, incluso si declara el tipo de retorno de una función, puede y lo olvidará después de haber escrito muchas líneas de código, y aún tendrá que volver a la línea en la que se declara utilizando la función de búsqueda de su editor de texto para verificarlo.
No se trata de que olvide el tipo de retorno; esto siempre va a suceder. Se trata de que la herramienta pueda hacerle saber que olvidó el tipo de devolución.
Además, a medida que las funciones se declaran con tipo funcname()...
, sin conocer el tipo tendrá que buscar sobre cada línea en la que se llama la función, porque solo sabe funcname
, mientras que en Python y similares simplemente puede buscar def funcname
o function funcname
que solo sucede una vez , en la declaración.
Esta es una cuestión de sintaxis, que no tiene relación alguna con la escritura estática.
La sintaxis de la familia C es de hecho hostil cuando desea buscar una declaración sin tener herramientas especializadas a su disposición. Otros idiomas no tienen este problema. Ver la sintaxis de la declaración de Rust:
fn funcname(a: i32) -> i32
Más aún, con REPLs es trivial probar una función para su tipo de retorno con diferentes entradas, mientras que con lenguajes de tipo estático necesitaría agregar algunas líneas de código y recompilar todo solo para saber el tipo declarado.
Cualquier idioma puede ser interpretado y cualquier idioma puede tener un REPL.
Entonces, aparte de conocer el tipo de retorno de una función que claramente no es un punto fuerte de los lenguajes estáticamente tipados, ¿cómo es realmente útil el tipeo estático en proyectos más grandes?
Contestaré de manera abstracta.
Un programa consta de varias operaciones y esas operaciones se presentan como están debido a algunas suposiciones que hace el desarrollador.
Algunos supuestos son implícitos y otros explícitos. Algunos supuestos se refieren a una operación cerca de ellos, algunos se refieren a una operación lejos de ellos. Una suposición es más fácil de identificar cuando se expresa explícitamente y lo más cerca posible de los lugares donde su valor de verdad es importante.
Un error es la manifestación de una suposición que existe en el programa pero que no se cumple en algunos casos. Para rastrear un error, necesitamos identificar la suposición errónea. Para eliminar el error, necesitamos eliminar esa suposición del programa o cambiar algo para que la suposición realmente se mantenga.
Me gustaría clasificar los supuestos en dos tipos.
El primer tipo son los supuestos que pueden o no ser válidos, dependiendo de las entradas del programa. Para identificar una suposición errónea de este tipo, necesitamos buscar en el espacio todas las entradas posibles del programa. Usando conjeturas educadas y pensamiento racional, podemos reducir el problema y buscar en un espacio mucho más pequeño. Pero aún así, a medida que un programa crece incluso un poco, su espacio de entrada inicial crece a un ritmo enorme, hasta el punto en que puede considerarse infinito para todos los fines prácticos.
El segundo tipo son los supuestos que definitivamente son válidos para todas las entradas, o son definitivamente erróneos para todas las entradas. Cuando identificamos una suposición de este tipo como errónea, ni siquiera necesitamos ejecutar el programa o probar ninguna entrada. Cuando identificamos una suposición de este tipo como correcta, tenemos que preocuparnos de un sospechoso menos cuando estamos rastreando un error ( cualquier error). Por lo tanto, es valioso tener tantos supuestos como sea posible pertenecer a este tipo.
Para poner un supuesto en la segunda categoría (siempre verdadero o siempre falso, independiente de las entradas), necesitamos una cantidad mínima de información para estar disponible en el lugar donde se realiza el supuesto. A través del código fuente de un programa, la información se vuelve obsoleta bastante rápido (por ejemplo, muchos compiladores no hacen análisis interprocediales, lo que hace que cualquier llamada sea un límite difícil para la mayoría de la información). Necesitamos una manera de mantener actualizada la información requerida (válida y cercana).
Una forma es tener la fuente de esta información lo más cerca posible del lugar donde se va a consumir, pero eso puede ser poco práctico para la mayoría de los casos de uso. Otra forma es repetir la información con frecuencia, renovando su relevancia en todo el código fuente.
Como ya puede adivinar, los tipos estáticos son exactamente eso: balizas de información de tipo dispersas en el código fuente. Esa información se puede utilizar para colocar la mayoría de los supuestos sobre la corrección de tipo en la segunda categoría, lo que significa que casi cualquier operación se puede clasificar como siempre correcta o siempre incorrecta con respecto a la compatibilidad de tipos.
Cuando nuestros tipos son incorrectos, el análisis nos ahorra tiempo al llamar la atención sobre el error más temprano que tarde. Cuando nuestros tipos son correctos, el análisis nos ahorra tiempo al garantizar que cuando se produce un error, podemos descartar inmediatamente los errores de tipo.