Conectivos, equivalências, dedução natural e provas em Lean
Christiano Braga · Mestrado em Sistemas e Computação · IME
Baseada em How To Prove It with Lean (HTPIwL), capítulo 1.
Software controla aeronaves, dispositivos médicos, sistemas financeiros e redes de comunicação. Erros custam dinheiro e vidas.
O teste examina finitas execuções de um programa que admite infinitas.
O teste de programas pode ser usado para mostrar a presença de erros, mas nunca para mostrar a sua ausência!
E. W. Dijkstra, Notes on Structured Programming, EWD249, 1970.
Enunciamos uma propriedade de um programa como uma proposição matemática.
Provamos que toda execução a satisfaz; a prova cobre todas as entradas de uma vez.
Um assistente de prova verifica cada passo com respeito às regras de uma lógica formal e ajuda a construir a prova interativamente.
Em uso corrente: Lean, Rocq (antigo Coq), Isabelle/HOL, Agda. Marcos: o micronúcleo seL4 e o compilador CompCert.
Modelos de linguagem escrevem hoje uma parcela crescente do código. Plausível não é o mesmo que correto; o modo de falha é a alucinação.
O assistente de prova verifica a prova independentemente de como o código surgiu, então código errado não passa.
O ônus da correção move-se de ler o código para escrever a especificação certa.
Uma proposição é uma sentença declarativa que é verdadeira ou falsa. Em Lean o tipo Prop as classifica.
Conectivos constroem proposições compostas: ¬P, P ∧ Q, P ∨ Q, P → Q, P ↔ Q.
São funções de verdade, então o valor de uma proposição composta depende apenas dos valores das suas partes.
Uma implicação de antecedente falso é verdadeira, diga o que disser o consequente, porque uma implicação nada afirma sobre esses casos.
A disjunção é inclusiva, então P ∨ Q também vale quando P e Q valem juntas.
Uma valoração atribui um valor de verdade a cada variável. Uma tautologia é verdadeira sob toda valoração.
A ≡ B quando A ↔ B é uma tautologia, isto é, as duas coincidem sob toda valoração.
Nome | Equivalência |
|---|---|
De Morgan | ¬(P ∧ Q) ≡ ¬P ∨ ¬Q |
De Morgan | ¬(P ∨ Q) ≡ ¬P ∧ ¬Q |
Dupla negação | ¬¬P ≡ P |
Contrapositiva | P → Q ≡ ¬Q → ¬P |
Implicação material | P → Q ≡ ¬P ∨ Q |
A implicação chama-se material porque a sua verdade depende apenas dos valores de verdade de P e Q, e não de alguma conexão de significado entre eles (Russell, 1903).
Segunda lei de De Morgan: as colunas de ¬(P ∨ Q) e ¬P ∧ ¬Q coincidem nas quatro valorações.
P | Q | P ∨ Q | ¬(P ∨ Q) | ¬P ∧ ¬Q |
|---|---|---|---|---|
V | V | V | F | F |
V | F | V | F | F |
F | V | V | F | F |
F | F | F | V | V |
Tabelas-verdade decidem qualquer questão proposicional, mas crescem exponencialmente no número de variáveis e não se estendem aos quantificadores da Aula 2. Um cálculo deriva em vez de computar.
Um cálculo fixa axiomas e regras de inferência. Uma derivação aplica as regras, nenhuma valoração aparece nela, e uma máquina pode verificá-la.
Ele é correto quando todo teorema é uma tautologia e completo quando toda tautologia é um teorema. Post provou as duas para o cálculo proposicional em 1921.
Um cálculo axiomático tem muitos axiomas e uma regra. Łukasiewicz e Tarski precisam de três esquemas sobre → e ¬, com o modus ponens.
A → (B → A) (A → (B → C)) → ((A → B) → (A → C)) (¬A → ¬B) → (B → A)
A resolução mantém uma única regra sobre cláusulas, e é com ela que provadores automáticos buscam (Robinson, 1965).
A dedução natural não tem axiomas e tem duas regras por conectivo, uma que o introduz e outra que o elimina. Esta disciplina a usa.
Cada regra tem premissas acima de uma linha e uma conclusão abaixo, aplicadas um passo por vez (Gentzen, 1935).
Regras de introdução provam um conectivo; regras de eliminação o usam. Algumas regras descartam uma suposição, marcada [P].
[P]
⋮
Q P → Q P
─────── →I ───────────── →E
P → Q Q
P Q P ∧ Q P ∧ Q ─────── ∧I ─────── ∧E₁ ─────── ∧E₂ P ∧ Q P Q
P Q [P] [Q]
─────── ∨I₁ ─────── ∨I₂ P ∨ Q ⋮ ⋮
P ∨ Q P ∨ Q R R
────────────────────────── ∨E
R
A constante ⊥ é o absurdo, e ¬P abrevia P → ⊥.
[P]
⋮
⊥ P ¬P ⊥
─────── ¬I ───────── ¬E ───── ⊥E
¬P ⊥ C
As regras acima são construtivas. A lógica clássica acrescenta mais uma regra, equivalentemente RAA ou o terceiro excluído.
[¬P]
⋮
⊥
───────── RAA ─────────── EM
P P ∨ ¬P
Uma declaração dá nome a um enunciado e apresenta a sua prova. A palavra-chave vem primeiro, depois o nome, depois as hipóteses entre parênteses, depois o enunciado após os dois-pontos, e por fim a prova após :=.
Prova por termo
theorem and_swap (P Q : Prop) (h : P ∧ Q) : Q ∧ P :=
⟨h.right, h.left⟩
fun h => e constrói uma função, e f a aplica uma.
⟨a, b⟩ é o construtor anônimo; h.left e h.right desmontam uma conjunção.
Prova por táticas
example (P Q : Prop) (h : P ∧ Q) :
Q ∧ P := P:PropQ:Proph:P ∧ Q⊢ Q ∧ P
All goals completed! 🐙
by entra no modo de táticas, · foca um objetivo, e sorry marca uma prova ausente.
#check imprime o tipo de um termo, e -- inicia um comentário.
Os símbolos lógicos são unicode, digitados com uma abreviação de contrabarra: \to para →, \and para ∧, \or para ∨, \not para ¬, \iff para ↔, \bot para ⊥, \langle e \rangle para ⟨ ⟩, e \. para ·.
Uma prova de uma proposição é um termo cujo tipo é aquela proposição; uma suposição é uma variável daquele tipo. Cada regra constrói ou desmonta um termo.
Regra | Termo em Lean | Exemplo |
|---|---|---|
suposição | um nome de hipótese |
|
→I |
|
|
→E | aplicação |
|
∧I |
|
|
∧E₁, ∧E₂ |
|
|
∨I₁, ∨I₂ |
|
|
∨E |
|
|
¬I |
|
|
¬E |
aplicação em |
|
⊥E |
|
|
1. P ∧ Q → Q ∧ P
[P ∧ Q] [P ∧ Q]
─────────∧E₂ ─────────∧E₁
Q P
────────────────────── ∧I
Q ∧ P
────────────────────────── →I
P ∧ Q → Q ∧ P
example (P Q : Prop) : P ∧ Q → Q ∧ P :=
fun h => ⟨h.right, h.left⟩
fun h => é o →I que descarta P ∧ Q
h.right e h.left são ∧E₂ e ∧E₁
⟨_, _⟩ é o ∧I
2. P → P ∨ Q
[P]
─────── ∨I₁
P ∨ Q
─────────── →I
P → P ∨ Q
example (P Q : Prop) : P → P ∨ Q :=
fun h => Or.inl h
fun h => é o →I que descarta P
Or.inl é o ∨I₁, escolhendo o disjunto esquerdo
P → ¬¬P em detalhe
¬A é A → False. Duas vezes: ¬¬P é (P → False) → False, e o enunciado é P → ((P → False) → False).
Os parênteses são obrigatórios. Como → associa à direita, P → P → P → False é outra proposição, e falsa.
A derivação e o objetivo a cada passo
[¬P] [P]
──────────── ¬E
⊥
──────── ¬I
¬¬P
───────────── →I
P → ¬¬P
⊢ P → ¬¬P hP : P ⊢ ¬¬P hP : P, hnP : ¬P ⊢ False
Uma função por seta
example (P : Prop) : P → ¬¬P :=
fun (hP : P) =>
fun (hnP : ¬P) => hnP hP
o primeiro fun é o →I que descarta P
o segundo é o ¬I que descarta ¬P; o seu parâmetro tem tipo ¬P, e não P
hnP hP é o ¬E: uma hipótese negativa é uma função em False, e aplicá-la a hP dá ⊥
Uma tática transforma o estado de prova, o objetivo junto com as hipóteses em escopo, um passo por vez.
by entra no modo de táticas, e a sequência elabora para um termo de prova, então prova por táticas e prova por termos produzem o mesmo objeto.
exact e fecha o objetivo quando o tipo de e é o objetivo, e have h : A := e é o passo progressivo, que acrescenta h : A ao contexto sem mexer no objetivo.
apply f é o passo regressivo. Ela aplica f a argumentos em aberto, unifica a conclusão do tipo de f com o objetivo e deixa cada premissa não resolvida como novo objetivo. Com objetivo Q e f : P → Q, o objetivo passa a P, e o termo em construção é hPQ ?p, que exact hP completa em hPQ hP.
example (P Q : Prop) (hPQ : P → Q)
(hP : P) : Q := P:PropQ:ProphPQ:P → QhP:P⊢ Q
P:PropQ:ProphPQ:P → QhP:P⊢ P
All goals completed! 🐙
⊢ Q apply hPQ ⊢ P exact hP
Conectivo | Introduzir | Eliminar |
|---|---|---|
→ |
|
|
∧ |
|
|
∨ |
|
|
¬ |
|
|
⊥ | (nenhum) |
|
example (P Q : Prop) : P ∨ Q → Q ∨ P := P:PropQ:Prop⊢ P ∨ Q → Q ∨ P
P:PropQ:Proph:P ∨ Q⊢ Q ∨ P
cases h with
P:PropQ:ProphP:P⊢ Q ∨ P All goals completed! 🐙
P:PropQ:ProphQ:Q⊢ Q ∨ P All goals completed! 🐙
example (P : Prop) : ¬¬P → P := P:Prop⊢ ¬¬P → P
P:Proph:¬¬P⊢ P
P:Proph:¬¬P⊢ ¬P → False
P:Proph:¬¬PhnP:¬P⊢ False
All goals completed! 🐙
Táticas de introdução constroem o objetivo; táticas de eliminação usam uma hipótese. O raciocínio clássico acrescenta Classical.byContradiction e Classical.em, necessários para a eliminação da dupla negação.
Uma prova por táticas se desenrola como um jogo de tabuleiro. Cada elemento do jogo nomeia uma parte precisa da prova.
Jogo de tabuleiro | Prova por táticas |
|---|---|
O tabuleiro | o estado de prova, o objetivo junto com as hipóteses em escopo |
Suas peças | as hipóteses que você pode usar |
Uma jogada |
uma tática ( |
Dividir o tabuleiro | uma tática que abre vários objetivos; cada um precisa ser vencido |
Duas direções |
|
O livro de regras | as regras de introdução e eliminação da dedução natural |
Vencer | todo objetivo fechado, e o kernel de Lean verifica o termo de prova final |
Derivação
[P ∧ Q]
────────── ∧E₁
P
──────────── →I
P ∧ Q → P
Modo de termos
example (P Q : Prop) : P ∧ Q → P :=
fun h => h.left
fun h => é o →I que descarta P ∧ Q
h.left é o ∧E₁, a projeção esquerda
Modo de táticas
example (P Q : Prop) : P ∧ Q → P := P:PropQ:Prop⊢ P ∧ Q → P
P:PropQ:Proph:P ∧ Q⊢ P
All goals completed! 🐙
intro h é o →I que descarta P ∧ Q
exact h.left fecha o objetivo por ∧E₁
Derivação
[⊥]
────── ⊥E
P
──────── →I
⊥ → P
Modo de termos
example (P : Prop) : False → P :=
fun h => False.elim h
fun h => é o →I que descarta ⊥
False.elim h é o ⊥E, dando qualquer P
Modo de táticas
example (P : Prop) : False → P := P:Prop⊢ False → P
P:Proph:False⊢ P
All goals completed! 🐙
intro h é o →I que descarta ⊥
exact False.elim h fecha o objetivo por ⊥E
Derivação
[(P→Q)∧P] [(P→Q)∧P]
───────────── ∧E₁ ───────────── ∧E₂
P → Q P
────────────────────────────── →E
Q
──────────────────────────────── →I
(P → Q) ∧ P → Q
Modo de termos
example (P Q : Prop) : (P → Q) ∧ P → Q :=
fun h => h.left h.right
fun h => é o →I que descarta (P → Q) ∧ P
h.left e h.right são ∧E₁ e ∧E₂
a aplicação h.left h.right é o →E, modus ponens
Modo de táticas
example (P Q : Prop) : (P → Q) ∧ P → Q := P:PropQ:Prop⊢ (P → Q) ∧ P → Q
P:PropQ:Proph:(P → Q) ∧ P⊢ Q
P:PropQ:Proph:(P → Q) ∧ P⊢ P
All goals completed! 🐙
intro h é o →I que descarta (P → Q) ∧ P
apply h.left reduz o objetivo a P por →E
exact h.right fornece P por ∧E₂
Derivação
[P] [Q]
[P ∨ Q] ─────── ∨I₂ ─────── ∨I₁
Q ∨ P Q ∨ P
───────────────────────────────────── ∨E
Q ∨ P
────────────────────── →I
P ∨ Q → Q ∨ P
Modo de termos
example (P Q : Prop) : P ∨ Q → Q ∨ P :=
fun h => h.elim
(fun hP => Or.inr hP)
(fun hQ => Or.inl hQ)
fun h => é o →I que descarta P ∨ Q
h.elim é o ∨E, um ramo por disjunto
Or.inr e Or.inl são ∨I, trocando os disjuntos
Modo de táticas
example (P Q : Prop) : P ∨ Q → Q ∨ P := P:PropQ:Prop⊢ P ∨ Q → Q ∨ P
P:PropQ:Proph:P ∨ Q⊢ Q ∨ P
cases h with
P:PropQ:ProphP:P⊢ Q ∨ P All goals completed! 🐙
P:PropQ:ProphQ:Q⊢ Q ∨ P All goals completed! 🐙
intro h é o →I que descarta P ∨ Q
cases h é o ∨E, dividindo nos dois disjuntos
cada ramo fecha com Or.inr / Or.inl (∨I)
Derivação
[¬P] [¬¬P]
────────────── ¬E
⊥
────────── RAA
P
─────────────── →I
¬¬P → P
Modo de termos
example (P : Prop) : ¬¬P → P :=
fun h => Classical.byContradiction (fun hnP => h hnP)
fun h => é o →I que descarta ¬¬P
Classical.byContradiction é a regra clássica (RAA)
fun hnP => h hnP deriva ⊥ de ¬P e ¬¬P por ¬E
Modo de táticas
example (P : Prop) : ¬¬P → P := P:Prop⊢ ¬¬P → P
P:Proph:¬¬P⊢ P
P:Proph:¬¬P⊢ ¬P → False
P:Proph:¬¬PhnP:¬P⊢ False
All goals completed! 🐙
intro h é o →I que descarta ¬¬P
apply Classical.byContradiction invoca a RAA
intro hnP e então exact h hnP derivam ⊥ por ¬E
Uma proposição é classificada por Prop; os conectivos ¬, ∧, ∨, →, ↔ constroem proposições compostas.
Tabelas-verdade decidem questões proposicionais, mas crescem exponencialmente; a dedução natural aplica regras um passo por vez e generaliza.
Cada conectivo tem regras de introdução e eliminação; algumas regras descartam suposições.
Uma prova é um termo cujo tipo é a proposição (Curry-Howard): fun h => e para →I, aplicação para →E, ⟨_, _⟩ para ∧I, Or.inl/Or.inr para ∨I.
Táticas transformam o estado de prova: intro, exact, apply, cases, constructor, have; by as elabora para o mesmo termo de prova.
O raciocínio clássico acrescenta Classical.byContradiction e Classical.em, necessários para ¬¬P → P e uma lei de De Morgan.
Exercícios: veja as notas de aula.