Assim como o título diz: que garantias existem para que uma unidade de retorno da função Haskell seja avaliada? Alguém poderia pensar que não há necessidade de executar qualquer tipo de avaliação nesse caso; o compilador poderia substituir todas essas chamadas por um ()
valor imediato, a menos que existam solicitações explícitas de rigor; nesse caso, o código pode ter que decidir se deve retorno ()
ou fundo.
Eu experimentei isso no GHCi, e parece que acontece o contrário, ou seja, essa função parece ser avaliada. Um exemplo muito primitivo seria
f :: a -> ()
f _ = undefined
A avaliação f 1
gera um erro devido à presença de undefined
, portanto, alguma avaliação definitivamente acontece. Não está claro até que ponto a avaliação é profunda; Às vezes, parece ser tão profundo quanto necessário para avaliar todas as chamadas para funções retornando ()
. Exemplo:
g :: [a] -> ()
g [] = ()
g (_:xs) = g xs
Este código faz um loop indefinidamente se apresentado g (let x = 1:x in x)
. Mas então
f :: a -> ()
f _ = undefined
h :: a -> ()
h _ = ()
pode ser usado para mostrar que h (f 1)
retornos ()
; portanto, neste caso, nem todas as subexpressões com valor unitário são avaliadas. Qual é a regra geral aqui?
ETA: é claro que eu sei sobre preguiça. Estou perguntando o que impede os escritores de compiladores de tornar esse caso em particular ainda mais preguiçoso do que o normalmente possível.
ETA2: resumo dos exemplos: O GHC parece tratar ()
exatamente como qualquer outro tipo, ou seja, como se houvesse uma pergunta sobre qual valor regular que habita o tipo deve ser retornado de uma função. O fato de existir apenas um desses valores não parece ser (ab) usado pelos algoritmos de otimização.
ETA3: quando digo Haskell, quero dizer Haskell como definido pelo relatório, não Haskell-the-H-in-GHC. Parece ser uma suposição não compartilhada tão amplamente quanto eu imaginava (que era "em 100% dos leitores"), ou eu provavelmente teria sido capaz de formular uma pergunta mais clara. Mesmo assim, lamento alterar o título da pergunta, pois ela originalmente perguntou quais garantias existem para que uma função seja chamada.
ETA4: parece que essa pergunta seguiu seu curso, e eu estou considerando isso sem resposta. (Eu estava procurando por uma função de 'pergunta próxima', mas só encontrei 'responda sua própria pergunta' e, como ela não pode ser respondida, não segui esse caminho.) , que sou tentado a interpretar como uma resposta forte, mas não definitiva, 'sem garantia para o idioma em si'. Tudo o que sabemos é que a implementação atual do GHC não ignorará a avaliação de uma função desse tipo.
Eu encontrei o problema real ao portar um aplicativo OCaml para Haskell. O aplicativo original tinha uma estrutura mutuamente recursiva de muitos tipos, e o código declarou várias funções chamadas assert_structureN_is_correct
N em 1 .. 6 ou 7, cada uma das quais retornou a unidade se a estrutura estivesse realmente correta e lançou uma exceção se não estivesse. . Além disso, essas funções se chamavam ao decompor as condições de correção. Em Haskell, isso é melhor tratado com a Either String
mônada, então transcrevi dessa maneira, mas a questão como questão teórica permaneceu. Obrigado por todas as entradas e respostas.
f 1
é "substituído" por undefined
em todos os casos.
... -> ()
pode 1) terminar e retornar ()
, 2) terminar com um erro de exceção / tempo de execução e falhar ao retornar qualquer coisa ou 3) divergir (recursão infinita). O GHC não otimiza o código, assumindo que apenas 1) pode acontecer: se f 1
for exigido, não pula sua avaliação e retorna ()
. A semântica de Haskell é avaliá-la e ver o que acontece entre 1,2,3.
()
(o tipo ou o valor) nesta questão. Todas as mesmas observações acontecem se você substituir () :: ()
por, por exemplo, 0 :: Int
todos os lugares. Essas são apenas velhas consequências chatas da avaliação preguiçosa.
()
tipo, ()
e undefined
.
h1::()->() ; h1 () = ()
eh2::()->() ; h2 _ = ()
. Execute ambosh1 (f 1)
eh2 (f 1)
, e veja que apenas o primeiro exige(f 1)
.