Notez que :sprint
cela ne réduit pas une expression à WHNF. Si c'était le cas, alors ce qui suit donnerait 4
plutôt que _
:
Prelude> let four = 2 + 2 :: Int
Prelude> :sprint four
four = _
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:
> :set -ddump-simpl -dsuppress-all -dsuppress-uniques
Après cela, nous pouvons voir la raison pour laquelle vous intlist
donne _
:
> let intlist = [[1,2],[2,3]]
==================== Simplified expression ====================
returnIO
(: ((\ @ a $dNum ->
: (: (fromInteger $dNum 1) (: (fromInteger $dNum 2) []))
(: (: (fromInteger $dNum 2) (: (fromInteger $dNum 3) [])) []))
`cast` <Co:10>)
[])
Vous pouvez ignorer l' appel returnIO
externe :
et vous concentrer sur la partie commençant par((\ @ a $dNum -> ...
Voici $dNum
le dictionnaire de la Num
contrainte. Cela signifie que le code généré n'a pas encore résolu le type réel a
dans le type Num a => [[a]]
, donc l'expression entière est toujours représentée comme un appel de fonction prenant un (dictionnaire pour) un Num
type approprié . En d'autres termes, c'est un thunk non évalué, et nous obtenons:
> :sprint intlist
_
D'un autre côté, spécifiez le type en tant que Int
, et le code est complètement différent:
> let intlist = [[1::Int,2],[2,3]]
==================== Simplified expression ====================
returnIO
(: ((: (: (I# 1#) (: (I# 2#) []))
(: (: (I# 2#) (: (I# 3#) [])) []))
`cast` <Co:6>)
[])
et la :sprint
sortie aussi:
> :sprint intlist
intlist = [[1,2],[2,3]]
De même, les chaînes littérales et les listes explicites de caractères ont des représentations complètement différentes:
> let stringlist = ["hi", "there"]
==================== Simplified expression ====================
returnIO
(: ((: (unpackCString# "hi"#) (: (unpackCString# "there"#) []))
`cast` <Co:6>)
[])
> let charlist = [['h','i'], ['t','h','e','r','e']]
==================== Simplified expression ====================
returnIO
(: ((: (: (C# 'h'#) (: (C# 'i'#) []))
(: (: (C# 't'#)
(: (C# 'h'#) (: (C# 'e'#) (: (C# 'r'#) (: (C# 'e'#) [])))))
[]))
`cast` <Co:6>)
[])
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 (les unpackCString#
thunks).