Tropecei em algumas coisas irritantes. Eu sei que o haskell funciona com a forma normal da cabeça fraca (WHNF) e sei o que é isso. Digitando o seguinte código no ghci (estou usando o comando: sprint, que reduz a expressão para WHNF para meu conhecimento.):
let intlist = [[1,2],[2,3]]
:sprint intlist
dá intlist = _
isso faz totalmente sentido para mim.
let stringlist = ["hi","there"]
:sprint stringlist
dá stringlist = [_,_]
Isso já me confunde. Mas então:
let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist
surpreendentemente dá charlist = ["hi","there"]
Tanto quanto eu entendi Haskell, strings nada mais são do que listas de caracteres, o que parece ser confirmado verificando os tipos "hi" :: [Char]
e ['h','i'] :: [Char]
.
Estou confuso, porque, no meu entendimento, todos os três exemplos acima são mais ou menos os mesmos (uma lista de listas) e, portanto, devem reduzir para o mesmo WHNF, ou seja, _. o que estou perdendo?
obrigado
"bla"
e['b','l','a']
sairia diferente."bla"
poderia estar sobrecarregado, mas['b','l','a']
é conhecido por ser umString
/[Char]
?['b', 'l', 'a']
pode estar sobrecarregado , e da mesma forma"bla"
só será sobrecarregado se-XOverloadedStrings
estiver ligado.Respostas:
Note-se que
:sprint
se não reduzir a uma expressão para WHNF. Se o fizesse, o seguinte daria, em4
vez de_
:Em vez disso,
:sprint
pega o nome de uma ligação, percorre a representação interna do valor da ligação e mostra as já "partes avaliadas" (ou seja, as partes que são construtoras) enquanto usa_
como espaço reservado para thunks não avaliados (ou seja, a função lenta suspensa) chamadas). Se o valor for completamente não avaliado, nenhuma avaliação será feita, nem mesmo para o WHNF. (E se o valor for completamente avaliado, você obterá isso, não apenas o WHNF.)O que você está observando em seus experimentos é uma combinação de tipos numéricos polimórficos versus monomórficos, diferentes representações internas para literais de string versus listas explícitas de caracteres etc. Basicamente, você está observando diferenças técnicas na forma como diferentes expressões literais são compiladas para código de bytes. Portanto, interpretar esses detalhes de implementação como algo que tem a ver com o WHNF vai confundir irremediavelmente você. Geralmente, você deve usar apenas
:sprint
como uma ferramenta de depuração, não como uma maneira de aprender sobre o WHNF e a semântica da avaliação Haskell.Se você realmente quer entender o que
:sprint
está fazendo, pode ativar alguns sinalizadores no GHCi para ver como as expressões estão realmente sendo tratadas e, portanto, eventualmente compiladas no bytecode:Depois disso, podemos ver o motivo pelo qual você
intlist
dá_
:Você pode ignorar a chamada
returnIO
externa:
e concentrar-se na parte que começa com((\ @ a $dNum -> ...
Aqui
$dNum
está o dicionário para aNum
restrição. Isso significa que o código gerado ainda não resolveu o tipo reala
no tipoNum a => [[a]]
; portanto, toda a expressão ainda é representada como uma chamada de função, utilizando um (dicionário para) umNum
tipo apropriado . Em outras palavras, é um thunk não avaliado e obtemos:Por outro lado, especifique o tipo como
Int
e o código é completamente diferente:e assim é a
:sprint
saída:Da mesma forma, cadeias literais e listas explícitas de caracteres têm representações completamente diferentes:
e as diferenças na
:sprint
saída representam artefatos dos quais partes da expressão que o GHCi considera avaliadas (:
construtores explícitos ) versus não avaliadas (osunpackCString#
thunks).fonte