Beachten Sie, dass :sprint
sich nicht um einen Ausdruck zu WHNF reduzieren. Wenn dies der Fall wäre, würde Folgendes 4
eher geben als _
:
Prelude> let four = 2 + 2 :: Int
Prelude> :sprint four
four = _
Nimmt vielmehr :sprint
den Namen einer Bindung, durchläuft die interne Darstellung des Bindungswerts und zeigt die bereits "bewerteten Teile" (dh die Teile, die Konstruktoren sind) an, während sie _
als Platzhalter für nicht bewertete Thunks (dh die angehaltene Lazy-Funktion) verwendet werden Anrufe). Wenn der Wert vollständig nicht bewertet wird, wird keine Bewertung durchgeführt, auch nicht für WHNF. (Und wenn der Wert vollständig ausgewertet wird, erhalten Sie das, nicht nur WHNF.)
Was Sie in Ihren Experimenten beobachten, ist eine Kombination aus polymorphen und monomorphen numerischen Typen, verschiedenen internen Darstellungen für Zeichenfolgenliterale im Vergleich zu expliziten Listen von Zeichen usw. Grundsätzlich beobachten Sie technische Unterschiede bei der Kompilierung verschiedener Literalausdrücke zu Bytecode. Die Interpretation dieser Implementierungsdetails als etwas, das mit WHNF zu tun hat, wird Sie also hoffnungslos verwirren. Im Allgemeinen sollten Sie :sprint
nur als Debugging-Tool verwenden, nicht als Möglichkeit, sich mit WHNF und der Semantik der Haskell-Evaluierung vertraut zu machen.
Wenn Sie wirklich verstehen möchten, was :sprint
passiert, können Sie einige Flags in GHCi aktivieren, um zu sehen, wie Ausdrücke tatsächlich behandelt werden, und so schließlich zu Bytecode kompiliert werden:
> :set -ddump-simpl -dsuppress-all -dsuppress-uniques
Danach können wir den Grund sehen, den Sie intlist
angeben _
:
> 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>)
[])
Sie können den returnIO
und den äußeren :
Aufruf ignorieren und sich auf den Teil konzentrieren, der mit beginnt((\ @ a $dNum -> ...
Hier $dNum
ist das Wörterbuch für die Num
Einschränkung. Dies bedeutet, dass der generierte Code den tatsächlichen Typ a
im Typ noch nicht aufgelöst hat Num a => [[a]]
, sodass der gesamte Ausdruck weiterhin als Funktionsaufruf dargestellt wird, der ein (Wörterbuch für) einen geeigneten Num
Typ verwendet. Mit anderen Worten, es ist ein unbewerteter Thunk, und wir bekommen:
> :sprint intlist
_
Geben Sie andererseits den Typ als an Int
, und der Code ist völlig anders:
> let intlist = [[1::Int,2],[2,3]]
==================== Simplified expression ====================
returnIO
(: ((: (: (I# 1#) (: (I# 2#) []))
(: (: (I# 2#) (: (I# 3#) [])) []))
`cast` <Co:6>)
[])
und so ist die :sprint
Ausgabe:
> :sprint intlist
intlist = [[1,2],[2,3]]
Ebenso haben Literalzeichenfolgen und explizite Zeichenlisten völlig unterschiedliche Darstellungen:
> 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>)
[])
und die Unterschiede in der :sprint
Ausgabe stellen Artefakte dar, von denen Teile des Ausdrucks, die GHCi als bewertet (explizite :
Konstruktoren) betrachtet, als nicht bewertet (die unpackCString#
Thunks) betrachten.