J'ai trébuché sur des choses irritantes. Je sais que haskell fonctionne avec une forme normale de tête faible (WHNF) et je sais ce que c'est. Taper le code suivant dans ghci (j'utilise la commande: sprint qui réduit l'expression à WHNF à ma connaissance.):
let intlist = [[1,2],[2,3]]
:sprint intlist
intlist = _
cela me donne tout son sens.
let stringlist = ["hi","there"]
:sprint stringlist
donne stringlist = [_,_]
Cela me confond déjà. Mais alors:
let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist
donne étonnamment charlist = ["hi","there"]
Pour autant que j'ai compris Haskell, les chaînes ne sont rien d'autre que des listes de caractères, ce qui semble être confirmé en vérifiant les types "hi" :: [Char]
et ['h','i'] :: [Char]
.
Je suis confus, car à ma connaissance, les trois exemples ci-dessus sont plus ou moins les mêmes (une liste de listes) et devraient donc se réduire au même WHNF, à savoir _. Qu'est-ce que je rate?
Merci
"bla"
et['b','l','a']
sortirait différemment."bla"
pourrait être surchargé, mais['b','l','a']
est connu pour être unString
/[Char]
?['b', 'l', 'a']
pourrait également être surchargé , et"bla"
n'est également surchargé que s'il-XOverloadedStrings
est activé.Réponses:
Notez que
:sprint
cela ne réduit pas une expression à WHNF. Si c'était le cas, alors ce qui suit donnerait4
plutôt que_
:Prend plutôt
:sprint
le nom d'une liaison, parcourt la représentation interne de la valeur de la liaison et montre les "parties déjà évaluées" (c'est-à-dire les parties qui sont des constructeurs) tout en l'utilisant_
comme espace réservé pour les thunks non évalués (c'est-à-dire la fonction paresseuse suspendue appels). Si la valeur est complètement non évaluée, aucune évaluation ne sera effectuée, pas même pour WHNF. (Et si la valeur est complètement évaluée, vous l'obtiendrez, pas seulement WHNF.)Ce que vous observez dans vos expériences est une combinaison de types numériques polymorphes et monomorphes, de différentes représentations internes pour les littéraux de chaîne par rapport à des listes explicites de caractères, etc. Donc, interpréter ces détails d'implémentation comme ayant quelque chose à voir avec WHNF va désespérément vous confondre. En règle générale, vous devez
:sprint
uniquement utiliser un outil de débogage, et non pas un moyen d'en savoir plus sur WHNF et la sémantique de l'évaluation Haskell.Si vous voulez vraiment comprendre ce qui
:sprint
se passe, vous pouvez activer quelques indicateurs dans GHCi pour voir comment les expressions sont réellement gérées et, par conséquent, éventuellement compilées en bytecode:Après cela, nous pouvons voir la raison pour laquelle vous
intlist
donne_
:Vous pouvez ignorer l' appel
returnIO
externe:
et vous concentrer sur la partie commençant par((\ @ a $dNum -> ...
Voici
$dNum
le dictionnaire de laNum
contrainte. Cela signifie que le code généré n'a pas encore résolu le type réela
dans le typeNum a => [[a]]
, donc l'expression entière est toujours représentée comme un appel de fonction prenant un (dictionnaire pour) unNum
type approprié . En d'autres termes, c'est un thunk non évalué, et nous obtenons:D'un autre côté, spécifiez le type en tant que
Int
, et le code est complètement différent:et la
:sprint
sortie aussi:De même, les chaînes littérales et les listes explicites de caractères ont des représentations complètement différentes:
et les différences dans la
:sprint
sortie représentent des artefacts dont des parties de l'expression que GHCi considère évaluées (:
constructeurs explicites ) par opposition à non évaluées (lesunpackCString#
thunks).la source