La seguridad de tipos de Haskell es insuperable solo para los lenguajes de escritura dependiente. Pero hay algo de magia profunda sucediendo con Text.Printf que parece bastante inestable .
> printf "%d\n" 3
3
> printf "%s %f %d" "foo" 3.3 3
foo 3.3 3
¿Cuál es la magia profunda detrás de esto? ¿Cómo puede elText.Printf.printf función tomar argumentos variados como este?
¿Cuál es la técnica general utilizada para permitir argumentos variados en Haskell y cómo funciona?
(Nota al margen: aparentemente se pierde algún tipo de seguridad cuando se usa esta técnica).
> :t printf "%d\n" "foo"
printf "%d\n" "foo" :: (PrintfType ([Char] -> t)) => t
haskell
printf
variadic-functions
polyvariadic
Dan Burton
fuente
fuente

Respuestas:
El truco consiste en utilizar clases de tipos. En el caso de
printf, la clave es laPrintfTypeclase de tipo. No expone ningún método, pero la parte importante está en los tipos de todos modos.También
printftiene un tipo de retorno sobrecargado. En el caso trivial, no tenemos argumentos adicionales, por lo que debemos poder crearruna instancia deIO (). Para esto, tenemos la instanciaA continuación, para admitir un número variable de argumentos, debemos utilizar la recursividad a nivel de instancia. En particular, necesitamos una instancia para que si
res aPrintfType, un tipo de funciónx -> rtambién sea aPrintfType.Por supuesto, solo queremos admitir argumentos que realmente se puedan formatear. Ahí es donde
PrintfArgentra en juego la segunda clase de tipos . Así que la instancia real esAquí hay una versión simplificada que toma cualquier número de argumentos en la
Showclase y simplemente los imprime:Aquí,
bartoma una acción IO que se construye de forma recursiva hasta que no hay más argumentos, momento en el que simplemente la ejecutamos.QuickCheck también usa la misma técnica, donde la
Testableclase tiene una instancia para el caso baseBooly una recursiva para funciones que toman argumentos en laArbitraryclase.fuente
printf "%d" True. Esto es muy místico para mí, ya que parece que el valor de tiempo de ejecución (?) Se"%d"descifra en tiempo de compilación para requerir unInt. Esto es absolutamente desconcertante para mí. . . especialmente porque el código fuente no usa cosas comoDataKindsoTemplateHaskell(revisé el código fuente, pero no loprintf "%d" Truees porque no hay unaBoolinstancia dePrintfArg. Si pasa un argumento del tipo equivocado de que no tiene una instancia dePrintfArg, lo hace de compilación y se produce una excepción en tiempo de ejecución. Ej .:printf "%d" "hi"