Desta referência: Estrita positividade
A estrita condição de positividade exclui declarações como
data Bad : Set where
bad : (Bad → Bad) → Bad
A B C
-- A is in a negative position, B and C are OK
Por que A é negativo? Também porque B é permitido? Eu entendo por que C é permitido.
type-theory
inductive-datatypes
Pushpa
fonte
fonte
A
e explodir a pilha eventualmente (em idiomas baseados em pilha).Respostas:
Agora, para tipos de dados indutivos.
Na álgebra, é habitual que uma operação use um número finito de argumentos e, na maioria dos casos, use zero (constante), um (unário) ou dois (binário). É conveniente generalizar isso para construtores de tipos de dados. Suponha que
c
seja um construtor para um tipo de dadosT
:c
é uma constante, podemos pensar nisso como uma funçãounit -> T
, ou equivalente(empty -> T) -> T
,c
é unário, podemos pensar nisso como uma funçãoT -> T
, ou equivalente(unit -> T) -> T
,c
é binário, podemos pensar nisso como uma funçãoT -> T -> T
, ou equivalenteT * T -> T
, ou equivalente(bool -> T) -> T
,c
que receba sete argumentos, poderíamos vê-lo como uma função em(seven -> T) -> T
queseven
há algum tipo definido anteriormente com sete elementos.c
que receba muitos argumentos, infinitamente, que seriam uma função(nat -> T) -> T
.Esses exemplos mostram que a forma geral de um construtor deve ser
onde chamamos
A
a arity dec
e pensamosc
como um construtor que recebeA
argumentos -muitos do tipoT
para produzir um elemento deT
.Aqui está algo muito importante: as aridades devem ser definidas antes de definirmos
T
, ou então não podemos dizer o que os construtores devem estar fazendo. Se alguém tentar ter um construtorentão a pergunta "quantos argumentos são
broken
necessários?" não tem boa resposta. Você pode tentar respondê-lo com "são necessáriosT
muitos argumentos", mas isso nãoT
serve , porque ainda não está definido. Podemos tentar sair do cunundrum usando a teoria de ponto fixo sofisticada para encontrar um tipoT
e uma função injetiva(T -> T) -> T
, e teríamos sucesso, mas também quebraríamos o princípio da indução aoT
longo do caminho. Portanto, é apenas uma má idéia tentar uma coisa dessas.c
B
Na verdade, muitos construtores pode ser reescrita dessa maneira, mas não todos, precisamos de mais um passo, ou seja, devemos permitir
A
que dependem emB
:Esta é a forma final de um construtor para um tipo indutivo. Também é precisamente o que são os tipos W. O formulário é tão geral que precisamos apenas de um único construtor
c
! De fato, se tivermos dois delesentão podemos combiná-los em um
Onde
By the way, se curry a forma geral, vemos que é equivalente a
que é mais próximo do que as pessoas realmente escrevem em assistentes de prova. Os assistentes de prova nos permitem escrever os construtores de maneira conveniente, mas esses são equivalentes à forma geral acima (exercício!).
fonte
A primeira ocorrência de
Bad
é chamada 'negativa' porque representa um argumento de função, ou seja, está localizada à esquerda da seta da função (consulte Tipos recursivos gratuitamente por Philip Wadler). Eu acho que a origem do termo 'posição negativa' deriva da noção de contravariância ('contra' significa oposto).Não é permitido que o tipo seja definido em posição negativa, porque é possível escrever programas não-terminativos usando-o, ou seja, uma forte normalização falha em sua presença (mais sobre isso abaixo). A propósito, esse é o motivo do nome da regra 'estrita positividade': não permitimos posições negativas.
Permitimos a segunda ocorrência de,
Bad
uma vez que não causa terminação e queremos usar o tipo que está sendo definido (Bad
) em algum momento de um tipo de dados recursivo ( antes da última seta do construtor).É importante entender que a seguinte definição não viola a regra estrita de positividade.
A regra se aplica somente aos argumentos do construtor (que são os dois
Good
nesse caso) e não ao próprio construtor (consulte também " Programação Certificada com Tipos Dependentes ", de Adam Chlipala ).Outro exemplo que viola uma estrita positividade:
Você também pode verificar esta resposta sobre posições negativas.
Mais sobre não rescisão ... A página que você referenciou contém algumas explicações (junto com um exemplo em Haskell):
Aqui está um exemplo análogo no Ocaml, que mostra como implementar o comportamento recursivo sem (!) Usando a recursão diretamente:
A
nonTerminating
função "desempacota" uma função de seu argumento e a aplica ao argumento original. O que é importante aqui é que a maioria dos sistemas de tipos não permite a passagem de funções para si mesmos; portanto, um termo comof f
não será checado tipicamente, pois não há um tipo paraf
satisfazer o checador tipográfico. Um dos motivos pelos quais os sistemas de tipos foram introduzidos é desativar a autoaplicação (veja aqui ).O agrupamento de tipos de dados como o que introduzimos acima pode ser usado para contornar esse obstáculo no caminho para a inconsistência.
Quero acrescentar que cálculos não-térmicos introduzem inconsistências nos sistemas lógicos. No caso da Agda e da Coq, o
False
tipo de dados indutivo não possui construtores; portanto, você nunca pode criar um termo de prova do tipo False. Mas se cálculos não-terminantes fossem permitidos, seria possível, por exemplo, desta maneira (no Coq):Depois
loop 0
, verificava-se a doaçãoloop 0 : False
, portanto, sob a correspondência de Curry-Howard, isso significava que provávamos uma proposição falsa.Resultado : a regra estrita de positividade para definições indutivas impede cálculos sem fim que são desastrosos para a lógica.
fonte