Todavía es una ventaja, incluso si escribe firmas de tipo, porque el compilador detectará errores de tipo en sus funciones. Normalmente también escribo firmas de tipo, pero las omito en lugares como where
o let
donde define realmente nuevos símbolos pero no siente la necesidad de especificar una firma de tipo.
ejemplo estúpida con una extraña manera de calcular cuadrados de los números:
squares :: [Int]
squares = sums 0 odds
where
odds = filter odd [1..]
sums s (a:as) = s : sums (s+a) as
square :: Int -> Int
square n = squares !! n
odds
y sums
son funciones que necesitarían una firma tipo si el compilador no podría inferir de forma automática.
Además, si usa funciones genéricas, como suele hacer, la inferencia de tipo es lo que asegura que realmente combine todas esas funciones genéricas juntas de una manera válida. Si, en el ejemplo anterior, digamos
squares :: [a]
squares = ...
El compilador puede deducir que esto no es válida esta manera, porque una de las funciones utilizadas (la función odd
de la librería estándar), necesita estar en a
la clase de tipo Integral
. En otros idiomas, por lo general, solo reconoces esto en un momento posterior.
Si escribe esto como una plantilla en C++, obtendrá un error de compilación cuando use la función en un tipo no integral, pero no cuando defina la plantilla. Esto puede ser bastante confuso, porque no está claro de inmediato dónde se ha equivocado y es posible que tenga que revisar una larga cadena de mensajes de error para encontrar la verdadera fuente del problema. Y en algo así como python se obtiene el error en tiempo de ejecución en algún punto inesperado, porque algo no tenía las funciones miembro esperadas. Y en los lenguajes aún más vagos, es posible que no obtenga ningún error, sino solo resultados inesperados.
En Haskell, el compilador puede garantizar que la función puede llamarse con todos los tipos especificados en su firma, incluso si es una función genérica que es válida para todos los tipos que cumplen algunas restricciones (también conocidas como clases de tipo). Esto facilita la programación de manera genérica y el uso de bibliotecas genéricas, algo mucho más difícil de conseguir en otros idiomas. Incluso si especifica una firma de tipo genérico, todavía hay mucha inferencia de tipo en curso en el compilador para averiguar qué tipo específico se utiliza en cada llamada y si este tipo cumple todos los requisitos de la función.