Conectivos Lógicos
Conjunção
A conjunção (ou lógico e) em kind recebe duas proposições a e b, que devem retornar um valor booleano true
ou false
.
Bool.and (a: Bool) (b: Bool) : Bool
Bool.and Bool.true b = b
Bool.and Bool.false b = Bool.false
Se a é verdadeiro, basta apenas retornar o valor de b, agora se o a for falso, não há a necessidade de verificar o valor do segundo elemento.
Por se tratar de um caso limitado (há apenas duas opções) dá para verificar com uma prova simples:
ConjuntiveBool : Equal Bool (Bool.and Bool.true Bool.false) Bool.false
ConjuntiveBool = Equal.refl
And_exercise
And_exercise : Pair (Equal (Plus 3n 4n) 7n) (Equal (Mult 2n 2n) 4n)
And_exercise = ?
Tanto para provar declarações conjuntivas. Para ir na outra direção – ou seja, usar uma hipótese conjuntiva para ajudar a provar outra coisa – empregamos o pattern matching. Se o contexto de prova contiver uma hipótese h da forma (a,b), a divisão de caso irá substituí-la por um padrão de pares (a,b).
And_example2 (n: Nat) (m: Nat) (e: Pair (Equal n 0n) (Equal m 0n)) : Equal (Plus n m ) 0n
And_example2 Nat.zero Nat.zero e = Equal.refl
And_example2 Nat.zero (Nat.succ m ) e =
let p = (Equal.rewrite
(Pair.snd e)
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
And_example2 (Nat.succ n) m e =
let p = (Equal.rewrite
(Pair.fst e)
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
Você pode se perguntar por que nos preocupamos em agrupar as duas hipóteses n = 0 e m = 0 em uma única conjunção, já que também poderíamos ter declarado o teorema com duas premissas separadas:
And_example2a (n: Nat) (m: Nat) (e1: Equal n 0n) (e2: Equal m 0n) : Equal (Plus n m) 0n
And_example2a Nat.zero Nat.zero e1 e2 = Equal.refl
And_example2a Nat.zero (Nat.succ m ) e1 e2 =
let p = (Equal.rewrite
(e2)
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
And_example2a (Nat.succ n) m e1 e2 =
let p = (Equal.rewrite
(e1)
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
Para este teorema, ambas as formulações são adequadas. Mas é importante entender como trabalhar com hipóteses conjuntivas porque as conjunções geralmente surgem de etapas intermediárias em provas, especialmente em desenvolvimentos maiores. Aqui está um exemplo simples:
And_example3 (n: Nat) (m: Nat) (e: Equal (Plus n m) 0n) : Equal (Mult n m) 0n
And_example3 Nat.zero m e = Equal.refl
And_example3 (Nat.succ n) m e =
let p = (Equal.rewrite
(e)
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
Outra situação comum com conjunções é que sabemos (a,b), mas em algum contexto precisamos apenas de a (ou apenas b). Os seguintes teoremas são úteis em tais casos:
Proj1 <p> <q> (a: Pair p q) : p
Proj1 (Pair.new fst snd) = fst
Proj2
Proj2 <p> <q> (b: Pair p q) : q
Proj2 (Pair.new fst snd) = ?
Por fim, às vezes precisamos reorganizar a ordem das conjunções e/ou agrupar as conjunções de múltiplas vias. Os seguintes teoremas de comutatividade e associatividade são úteis em tais casos.
And_commut <p> <q> (c: Pair p q) : Pair q p
And_commut (Pair.new fst snd) = Pair.new snd fst
And_assoc
And_assoc <p> <q> <r> (a: Pair p (Pair q r)) : Pair (Pair p q) r
And_assoc (Pair.new p (Pair q r) fst (Pair.new snd trd)) = ?
Disjunção
Outro conectivo importante é a disjunção, ou lógico, de duas proposições:
Either
a b é verdadeiro quando a ou b é. O primeiro caso foi marcado com left e o segundo com right.
Para usar uma hipótese disjuntiva em uma prova, procedemos pela análise de caso, que, como para Nat ou outros tipos de dados, pode ser feita com correspondência de padrões. Aqui está um exemplo:
Or_example (n: Nat) (m: Nat) (e: (Either (Equal n 0n) (Equal m 0n))) : Equal (Mult n m) 0n
Or_example Nat.zero m e = Equal.refl
Or_example n Nat.zero e = Mult_0_r n
Or_example (Nat.succ n) m (Either.left l r val) =
let p = (Equal.rewrite
(val)
( x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
Or_example (Nat.succ n) (Nat.succ m) (Either.right l r val) =
let p = (Equal.rewrite
(val)
( x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
Inversamente, para mostrar que uma disjunção é válida, precisamos mostrar que um de seus lados é válido. Isso pode ser feito por meio dos construtores Left e Right mencionados acima. Aqui está um uso trivial...
axiom
Or_intro_left <a> <b> (t: a) : Either a b
Or_intro_left t = Either.left t
axiom
Or_intro_right <a> <b> (t: b) : Either a b
Or_intro_right t = Either.right t
... e um exemplo um pouco mais interessante exigindo ambos
Zero_or_succ (n: Nat) : Either (Equal n 0n) (Equal n (Nat.succ (Nat.pred n)))
Zero_or_succ Nat.zero = Either.left Equal.refl
Zero_or_succ (Nat.succ n) = Either.right Equal.refl
Mult_eq_0
axiom
Mult_eq_0 (n: Nat) (m: Nat) (e: Equal (Mult n m) 0n) : Either (Equal n 0n) (Equal m 0n)
Mult_eq_0 n m e = ?
Or_commut
Or_commut <p> <q> (e: Either p q) : Either q p
Or_commut e: ?