É difícil acrescentar algo às explicações de Andrej ou Neel, mas vou tentar. Vou tentar abordar o ponto de vista sintático, em vez de tentar descobrir a semântica subjacente, porque a explicação é mais elementar e darei uma resposta mais direta à sua pergunta.
Eu estou indo para o trabalho no simplesmente tipado -calculus ao invés do sistema mais complexo subjacente Haskell. Eu acredito em particular que a presença de variáveis de tipo pode estar confundindo você até certo ponto.λ
A referência crucial é a seguinte:
Mendler, N. (1991). Tipos indutivos e restrições de tipo no cálculo lambda de segunda ordem. Não encontrei uma referência online, receio. As declarações e provas podem, no entanto, ser encontradas na tese de doutorado de Nax (uma leitura altamente recomendada!).
Mendler explica que a positividade é uma condição necessária e suficiente para o término na presença de definições de caso não recursivas (e definições recursivas estruturalmente decrescentes). Ele afirma usando uma formulação equacional . Dou um exemplo simples, que é uma simplificação do seu tipo .Bad
Bad=Bad→A
Onde é qualquer tipo. Temos entãoA
λx:Bad.x x:Bad→A
e entao
(λx:Bad.x x) (λx:Bad.x x):A
Mendler mostra que isso pode ser realizado para qualquer tipo
que é um tipo com pelo menos uma ocorrência negativa de (também pode haver ocorrências positivas) . Ele fornece um termo explícito que não termina com um (páginas 39-40 de sua tese).
Bad=F(Bad)
F(X)XF(X)
É claro que você está trabalhando não com tipos definidos equacionalmente, mas com construtores , ou seja, você tem
data Bad = Pack (Bad -> A)
ao invés de estrita igualdade. No entanto, você pode definir
unpack :: Bad -> (Bad -> A)
unpack (Pack f) = f
o que é suficiente para que esse resultado continue mantendo:
(\x:Bad -> unpack x x) (Pack (\x:Bad -> unpack x x))
Este termo ainda é bem digitado do tipo .A
No seu segundo exemplo, as coisas são um pouco mais complicadas, pois você tem algumas coisas ao longo das linhas de
Bad=Bad′→A
onde está relacionado , mas não igual, ao (no seu caso, eles são iguais a e respectivamente). Admito que não consegui construir um isomorfismo direto entre os dois. O mesmo problema está presente se você substituirB um d B um d um B um d ( N O t um)Bad′BadBad aBad (Not a)
type Not a = a -> False
com
data Not a = Not a
Seria facilmente resolvido se Haskell permitisse essas definições de tipo:
type Acc = Not Acc
Nesse caso, você pode criar um combinador de loop exatamente da mesma maneira que antes. Eu suspeito que você pode realizar uma construção semelhante (mas mais complexa) usando
data Acc = D (Not Acc)
O problema aqui é que, para construir um isomorfismo
Bad Acc <-> Bad (Not Acc)
você tem que lidar com variância mista.