tipos - if en haskell ejemplos
¿Cuándo aprovechar la inferencia de tipos en Haskell? (4)
Siempre escribo la firma de tipo para las funciones y valores de nivel superior, pero no para las cláusulas "dónde", "dejar" o "hacer".
En primer lugar, las funciones de nivel superior generalmente se exportan, y Haddock necesita una declaración de tipo para generar la documentación.
En segundo lugar, cuando comete un error, los errores del compilador son mucho más fáciles de decodificar si el compilador tiene información de tipo disponible. De hecho, a veces, en una cláusula complicada de "dónde" obtengo un error de tipo incomprensible, así que agrego declaraciones temporales de tipo para encontrar el problema, algo así como el equivalente a nivel de tipo de depuración de printf.
Entonces, para responder la pregunta original, uso la inferencia de tipo mucho pero no el 100% del tiempo.
Tengo curiosidad sobre la frecuencia con la que los programadores experimentados de Haskell usan la inferencia de tipos en la práctica. A menudo lo elogio como una ventaja sobre las declaraciones siempre explícitas que se necesitan en ciertos otros idiomas, pero por alguna razón (quizás solo porque soy nuevo) "se siente" bien escribir una firma de tipo casi todo el tiempo. .y estoy seguro de que en algunos casos es realmente necesario.
¿Pueden algunos Haskellers con experiencia (Haskellites, Haskellizers?) Proporcionar alguna información?
Tienes buenos instintos. Debido a que son verificados por el compilador, las firmas de tipo para los valores de nivel superior proporcionan una valiosa documentación.
Al igual que otros, casi siempre pongo una firma de tipo para una función de nivel superior, y casi nunca para ninguna otra declaración.
La inferencia de otro tipo de lugar es invaluable en el ciclo interactivo (por ejemplo, con GHCi). Esta técnica es más útil cuando estoy diseñando y depurando alguna nueva y lujosa función de orden superior o algo así.
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
clauses donde realmente defines nuevos símbolos pero no sientes la necesidad de especificar una firma de tipo.
Estúpido ejemplo con una forma extraña de calcular cuadrados de 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 de tipo si el compilador no las inferiría automáticamente.
Además, si usa funciones genéricas, como suele hacer, la inferencia de tipo es lo que garantiza que realmente combine todas esas funciones genéricas juntas de una manera válida. Si usted, en el ejemplo anterior, diga
squares :: [a]
squares = ...
El compilador puede deducir que esto no es válido de esta manera, porque una de las funciones utilizadas (la función odd
de la biblioteca estándar) necesita estar en 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 se pueda invocar con todos los tipos especificados en su firma, incluso si se trata de una función genérica que es válida para todos los tipos que cumplen algunas restricciones (también conocidas como clases de tipos). 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.
Cuando se enfrenta a un error de comprobación de tipo, aunque el compilador de Haskell proporciona información sobre el error, esta información puede ser difícil de descodificar. Para hacerlo más fácil, puede comentar la firma de tipo de la función y luego ver lo que el compilador ha deducido sobre el tipo y ver cómo difiere de su tipo previsto.
Otro uso es cuando construyes una ''función interna'' dentro de una función de nivel superior, pero no estás seguro de cómo construir la función interna o incluso cuál debe ser su tipo. Lo que puede hacer es pasar la función interna como un argumento para la función de nivel superior y luego pedir ghci para el tipo de función de nivel de tipo. Esto incluirá el tipo de la función interna. Luego puede usar una herramienta como Hoogle para ver si esta función ya existe en una biblioteca.