Me he tropezado con algunas cosas irritantes. Sé que Haskell funciona con la forma normal de la cabeza débil (WHNF) y sé qué es esto. Escribiendo el siguiente código en ghci (estoy usando el comando: sprint que reduce la expresión a WHNF que yo sepa):
let intlist = [[1,2],[2,3]]
:sprint intlist
da intlist = _esto tiene mucho sentido para mí.
let stringlist = ["hi","there"]
:sprint stringlist
da stringlist = [_,_]
Esto ya me confunde. Pero entonces:
let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist
sorprendentemente da charlist = ["hi","there"]
Hasta donde entendí Haskell, las cadenas no son más que listas de caracteres, lo que parece confirmarse al verificar los tipos "hi" :: [Char]y ['h','i'] :: [Char].
Estoy confundido porque, según tengo entendido, los tres ejemplos anteriores son más o menos lo mismo (una lista de listas) y, por lo tanto, deberían reducirse al mismo WHNF, es decir, _. ¿Qué me estoy perdiendo?
Gracias

"bla"y['b','l','a']saldría de otra manera."bla"podría estar sobrecargado, pero['b','l','a']se sabe que es unString/[Char]?['b', 'l', 'a']podría estar sobrecargado , y del mismo modo"bla"solo se sobrecarga si-XOverloadedStringsestá encendido.Respuestas:
Tenga en cuenta que
:sprintno no reducir a una expresión WHNF. Si lo hiciera, entonces lo siguiente daría en4lugar de_:Más bien,
:sprinttoma el nombre de un enlace, atraviesa la representación interna del valor del enlace y muestra las partes ya "evaluadas" (es decir, las partes que son constructoras) mientras se usa_como marcador de posición para troncos no evaluados (es decir, la función perezosa suspendida llamadas). Si el valor no está evaluado por completo, no se realizará ninguna evaluación, ni siquiera para WHNF. (Y si el valor se evalúa por completo, obtendrá eso, no solo WHNF).Lo que está observando en sus experimentos es una combinación de tipos numéricos polimórficos versus monomórficos, diferentes representaciones internas para literales de cadena versus listas explícitas de caracteres, etc. Básicamente, está observando diferencias técnicas en cómo se compilan diferentes expresiones literales para el código de bytes. Por lo tanto, interpretar estos detalles de implementación como algo que tiene que ver con WHNF te va a confundir irremediablemente. En general, debe usarlo solo
:sprintcomo una herramienta de depuración, no como una forma de aprender sobre WHNF y la semántica de la evaluación de Haskell.Si realmente quiere entender lo que
:sprintestá haciendo, puede activar algunos indicadores en GHCi para ver cómo se manejan las expresiones y, finalmente, compilarse en bytecode:Después de esto, podemos ver la razón por la que
intlistda_:Puede ignorar el
returnIOy la:llamada externa , y concentrarse en la parte que comienza con((\ @ a $dNum -> ...Aquí
$dNumestá el diccionario para laNumrestricción. Esto significa que el código generado aún no ha resuelto el tipo realaen el tipoNum a => [[a]], por lo que toda la expresión todavía se representa como una llamada de función que toma un (diccionario para) unNumtipo apropiado . En otras palabras, es un thunk no evaluado, y obtenemos:Por otro lado, especifique el tipo como
Int, y el código es completamente diferente:y también lo es la
:sprintsalida:Del mismo modo, las cadenas literales y las listas explícitas de caracteres tienen representaciones completamente diferentes:
y las diferencias en la
:sprintsalida representan artefactos de los cuales partes de la expresión GHCi considera evaluados (:constructores explícitos ) versus no evaluados (losunpackCString#thunks).fuente