2009-01-21 16 views
15

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.¿Cuándo aprovechar la inferencia de tipos en Haskell?

¿Pueden algunos Haskellers con experiencia (Haskellites? Haskellizers?) Proporcionar alguna entrada?

Respuesta

16

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.

5

Siempre escribo la firma de tipo para las funciones y valores de nivel superior, pero no para las cláusulas "where", "let" o "do".

Primero, 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, un poco como el equivalente a nivel de tipo de depuración de printf.

Para responder a la pregunta original, utilizo la inferencia de tipo mucho pero no el 100% del tiempo.

4

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.

Como 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 bucle 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í.

1

Cuando se enfrenta a un error de comprobación de tipo, aunque el compilador 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 se está construyendo una 'función interna' dentro de una función de nivel superior, pero no está 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.

Cuestiones relacionadas