Natknąłem się na niektóre irytujące rzeczy. Wiem, że haskell działa ze słabą normalną postacią głowy (WHNF) i wiem, co to jest. Wpisując następujący kod do ghci (używam polecenia: sprint, który redukuje wyrażenie do WHNF według mojej wiedzy.):
let intlist = [[1,2],[2,3]]
:sprint intlist
daje mi intlist = _
to całkowicie sens.
let stringlist = ["hi","there"]
:sprint stringlist
daje stringlist = [_,_]
To już mnie dezorientuje. Ale wtedy:
let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist
zaskakująco daje charlist = ["hi","there"]
O ile rozumiem Haskella, ciągi znaków są niczym innym jak listami znaków, co wydaje się potwierdzone przez sprawdzenie typów "hi" :: [Char]
i znaków ['h','i'] :: [Char]
.
Jestem zdezorientowany, ponieważ w moim rozumieniu wszystkie trzy powyższe przykłady są mniej więcej takie same (lista list) i dlatego powinny sprowadzać się do tego samego WHNF, a mianowicie _. czego mi brakuje?
Dzięki
"bla"
i['b','l','a']
wyjdzie inaczej."bla"
może być przeciążony, ale['b','l','a']
jest znany jakoString
/[Char]
?['b', 'l', 'a']
może być również przeciążone , podobnie jak"bla"
przeciążenie tylko wtedy, gdy-XOverloadedStrings
jest włączone.Odpowiedzi:
Zauważ, że
:sprint
nie nie zmniejszyć wyrażenie do WHNF. Gdyby tak było, to4
zamiast tego podano_
:Zamiast tego
:sprint
bierze nazwę powiązania, przegląda wewnętrzną reprezentację wartości powiązania i pokazuje już „ocenione części” (tj. Części, które są konstruktorami), jednocześnie wykorzystując_
jako symbol zastępczy dla nieocenionych zespołów (tj. Zawieszona leniwa funkcja połączenia). Jeśli wartość jest całkowicie nieoceniona, nie zostanie przeprowadzona ocena, nawet dla WHNF. (A jeśli wartość zostanie całkowicie oszacowana, dostaniesz to, nie tylko WHNF.)To, co obserwujesz w swoich eksperymentach, to kombinacja polimorficznych i monomorficznych typów liczbowych, różnych wewnętrznych reprezentacji literałów łańcuchowych w porównaniu z wyraźnymi listami znaków itp. Zasadniczo obserwujesz techniczne różnice w sposobie kompilacji różnych wyrażeń literalnych do kodu bajtowego. Tak więc interpretacja tych szczegółów implementacji jako mających coś wspólnego z WHNF spowoduje beznadziejne zamieszanie. Zasadniczo powinieneś używać wyłącznie
:sprint
jako narzędzie do debugowania, a nie jako sposób na poznanie WHNF i semantyki oceny Haskell.Jeśli naprawdę chcesz zrozumieć, co
:sprint
się dzieje, możesz włączyć kilka flag w GHCi, aby zobaczyć, w jaki sposób obsługiwane są wyrażenia, i ostatecznie skompilować je do kodu bajtowego:Następnie możemy zobaczyć powód, dla którego
intlist
podajesz_
:Możesz zignorować wywołanie
returnIO
zewnętrzne i zewnętrzne:
i skoncentrować się na części zaczynającej się od((\ @ a $dNum -> ...
Oto
$dNum
słownikNum
ograniczenia. Oznacza to, że wygenerowany kod nie rozwiązał jeszcze rzeczywistego typua
w tym typieNum a => [[a]]
, więc całe wyrażenie jest nadal reprezentowane jako wywołanie funkcji przyjmujące (słownik dla) odpowiedniegoNum
typu. Innymi słowy, jest to nieoceniony kawał, a otrzymujemy:Z drugiej strony określ typ jako
Int
, a kod jest zupełnie inny:podobnie jak
:sprint
wynik:Podobnie, dosłowne ciągi znaków i wyraźne listy znaków mają zupełnie inne reprezentacje:
a różnice w danych
:sprint
wyjściowych reprezentują artefakty, które części wyrażenia, które GHCi uważa za ocenione (:
konstruktory jawne ) w porównaniu do nieocenionych (thunksunpackCString#
).źródło