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-XOverloadedStrings
está encendido.Respuestas:
Tenga en cuenta que
:sprint
no no reducir a una expresión WHNF. Si lo hiciera, entonces lo siguiente daría en4
lugar de_
:Más bien,
:sprint
toma 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
:sprint
como 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
:sprint
está 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
intlist
da_
:Puede ignorar el
returnIO
y la:
llamada externa , y concentrarse en la parte que comienza con((\ @ a $dNum -> ...
Aquí
$dNum
está el diccionario para laNum
restricción. Esto significa que el código generado aún no ha resuelto el tipo reala
en 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) unNum
tipo 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
:sprint
salida:Del mismo modo, las cadenas literales y las listas explícitas de caracteres tienen representaciones completamente diferentes:
y las diferencias en la
:sprint
salida representan artefactos de los cuales partes de la expresión GHCi considera evaluados (:
constructores explícitos ) versus no evaluados (losunpackCString#
thunks).fuente