Truth
In addition to Empty, the standard library of Kind also defines Unit, a proposition that is trivially true. To prove this, we use the constant:
True_is_true : Unit
True_is_true = Unit.new
Unlike Empty, which is widely used, Unit
is rarely used in proofs since it is trivial (and therefore uninteresting) to prove as a goal and carries no useful information as a hypothesis. However, it can be quite useful when defining complex proofs using conditionals or as a parameter for higher-order proofs. We will see examples of this use of Unit
later.
Logical Equivalence
Logical Equivalence. The useful connective "if and only if", which asserts that two propositions have the same truth value, is just the conjunction of two implications.
record Equivalence (p: Type) (q: Type) {
lft: p -> q
rgt: q -> p
}
Equivalence.lft <p> <q> (e: Equivalence p q) : p
Equivalence.lft (Equivalence.new l r) = r (Equivalence.rgt (Equivalence.new l r))
Equivalence.rgt <p> <q> (e: Equivalence p q) : q
Equivalence.rgt (Equivalence.new l r) = l (Equivalence.lft (Equivalence.new l r))
Not_true_iff_false (b: Bool) : Equivalence (Not (Equal Bool b Bool.true)) (Equal Bool b Bool.false)
Not_true_iff_false b = Equivalence.new (x => Not_true_is_false b x) (y => Not_true_and_false b y)
Not_true_and_false (b : Bool) (e: Equal Bool b Bool.false) : Not (Equal Bool b Bool.true)
Not_true_and_false Bool.false Equal.refl =
emp =>
let p = Equal.rewrite emp
(x => match Bool x {
true => Empty
false => Unit
})
(Unit.new)
Empty.absurd p
Not_true_and_false Bool.true e =
let p = Equal.rewrite e
(x => if x
{Unit}
else
{Empty})
(Unit.new)
Empty.absurd p
Symmetry
A relation is symmetric if, for all elements p and q in its domain, if p is equivalent to q, then q is equivalent to p. This can be proved with the following rule:
Equivalence.mirror <p> <q> (e: Equivalence p q) : Equivalence q p
Equivalence.mirror p q (Equivalence.new lft rgt) = (Equivalence.new rgt lft)
Reflexivity
A relation is reflexive if, for every element p in its domain, p is equivalent to itself. This can be proved with the following rule:
Equivalence.refl <p> : Equivalence p p
Equivalence.refl p = ?
Transitivity
A relation is transitive if, for all elements p, q, and r in its domain, if p is equivalent to q and q is equivalent to r, then p is equivalent to r. This can be proved with the following rule:
Equivalence.chain <p> <q> <r> (e0: Equivalence p q) (e1: Equivalence q r) : Equivalence p r
Equivalence.chain p q r e0 e1 = ?
Or_distributes_over_and
Or_distributes_over_and <p> <q> <r> : Equivalence (Either p (Pair q r)) (Pair (Either p q) (Either p r))
Or_distributes_over_and p q r = ?
A special form of equivalence, avoiding the need for some low-level proof state manipulation. In particular, rewrite and reflexivity can be used with iff assertions, not just equalities. Here's a simple example demonstrating how these tactics work with iff. First, let's prove some basic iff equivalences:
Mult_0 (n: Nat) (m: Nat) : Equivalence (Equal Nat (Mult n m) 0n) (Either (Equal Nat n 0n) (Equal Nat m 0n))
Mult_0 n m = Equivalence.new (x => To_mult_0 n m x) (y => Or_example n m y)
To_mult_0 (n: Nat) (m: Nat) (e: Equal Nat (Mult n m) 0n) : (Either (Equal Nat n 0n) (Equal Nat m 0n))
To_mult_0 Nat.zero Nat.zero Equal.refl = Either.right Equal.refl
To_mult_0 Nat.zero (Nat.succ m) Equal.refl = Either.left Equal.refl
To_mult_0 (Nat.succ n) Nat.zero e = Either.right Equal.refl
To_mult_0 (Nat.succ n) (Nat.succ m) e =
let a = Plus_comm (Mult n (Nat.succ m)) (Nat.succ m)
let b = Equal.chain (Equal.mirror e) a
let c = (Equal.rewrite b
(x => match Nat x {
zero => Unit
succ => Empty
})
(Unit.new))
Empty.absurd c
Or_assoc <p> <q> <r> : Equivalence (Either p (Either q r)) (Either (Either p q) r)
Or_assoc = Equivalence.new (x => To_or_assoc x) (y => Fro_or_assoc y)
To_or_assoc <p> <q> <r> (e: Either p (Either q r)) : Either (Either p q) r
To_or_assoc (Either.left e) = Either.left (Either.left e)
To_or_assoc (Either.right p (Either q r) (Either.left e)) = Either.left (Either.right e)
To_or_assoc (Either.right p (Either q r) (Either.right e)) = Either.right e
Fro_or_assoc <p> <q> <r> (e: Either (Either p q) r) : Either p (Either q r)
Fro_or_assoc (Either.left (Either p q) r (Either.left e)) = Either.left e
Fro_or_assoc (Either.left (Either p q) r (Either.right e)) = Either.right (Either.left e)
Fro_or_assoc (Either.right (Either p q) r e) = Either.right (Either.right e)
Now we can use these facts with Equal.rewrite and Equal.refl to provide smooth proofs of assertions involving equivalences. Here's a ternary version of the previous result of Mult_0:
Mult_0_3 (n: Nat) (m: Nat) (p: Nat) : Equivalence (Equal Nat (Mult n (Mult m p)) 0n) (Either (Equal Nat n 0n) (Either (Equal Nat m 0n) (Equal Nat p 0n)))
Mult_0_3 n m p = Equivalence.new (x => To_mult_0_3 n m p x) (y => Fro_mult_0_3 n m p y)
To_mult_0_3 (n: Nat) (m: Nat) (p: Nat) (e: (Equal Nat (Nat.mul n (Nat.mul m p)) 0n)) : (Either (Equal Nat n 0n) (Either (Equal Nat m 0n) (Equal Nat p 0n)))
To_mult_0_3 n m p e = Either.swap (Equivalence.rgt (Or_assoc (Equal m 0n) (Equal p 0n) (Equal n 0n)))
Fro_mult_0_3 (n: Nat) (m: Nat) (p: Nat) (e: Either (Equal Nat n 0n) (Either (Equal Nat m 0n) (Equal Nat p 0n))) : (Equal Nat (Mult n (Mult m p)) 0n)
Fro_mult_0_3 (n: Nat) (m: Nat) (p: Nat) (e: Either (Equal Nat n 0n) (Either (Equal Nat m 0n) (Equal Nat p 0n))) : (Equal Nat (Nat.mul n (Nat.mul m p)) 0n)
Fro_mult_0_3 Nat.zero m p (Either.left Equal.refl) = Equal.refl
Fro_mult_0_3 n Nat.zero p (Either.right a (Either b c) (Either.left Equal.refl)) = Mult_comm 0n n
Fro_mult_0_3 n m Nat.zero (Either.right a (Either b c) (Either.right Equal.refl)) = Equal.chain (Mult_assoc n m 0n) (Mult_0_r (Nat.mul n m))
Fro_mult_0_3 (Nat.succ n) m p (Either.left e) =
let p = (Equal.rewrite
e
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
Fro_mult_0_3 n (Nat.succ m) p (Either.right a (Either b c) (Either.left e)) =
let p = (Equal.rewrite
e
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
Fro_mult_0_3 n m (Nat.succ p) (Either.right a (Either b c) (Either.right e)) =
let p = (Equal.rewrite
e
(x => match Nat x {
zero => Empty
succ => Unit
})
(Unit.new))
Empty.absurd p
The apply tactic can also be used with equivalence. When given an equivalence as its argument, apply tries to guess which side of the equivalence to use.
Apply_iff_example (n: Nat) (m: Nat) (e: Equal Nat (Mult n m) 0n) : Either (Equal Nat n 0n) (Equal Nat m 0n)
Apply_iff_example n m e = Equivalence.rgt (Mult_0 n m)
Existential Quantification
Another important logical connective is existential quantification. To say that there is some x of type Type such that some property p is true for x, we write (Sigma x p)
.
record Sigma (x: Type) (p: x -> Type) {
fst : x
snd : (p fst)
}
- Currently, the Kind-lang has a compiler bug that is affecting the lambda application of Sigma.
To prove a statement of the form (Sigma x p), we must show that p is true for some specific choice of value for x, known as the existential witness. This is done in two steps: First, we explicitly inform Kind of the witness x we have in mind by writing it in the first parameter. Then, we prove that p is true after all occurrences of x are replaced with Type.
Four_is_even : Sigma Nat (n => (Equal Nat 4n (Nat.add n n)))
Four_is_even = $ 2n Equal.refl
On the other hand, if we have an existential hypothesis (Sigma x p) in the context, we can pattern match on it to obtain a witness x and a hypothesis asserting that p is true for x.
Exists_example_2 (n: Nat) (m: Sigma Nat (m => (Equal Nat n (Nat.add 4n m)))) : Sigma Nat (o => (Equal Nat n (Nat.add 2n o)))
Exists_example_2 n (Sigma.new Nat s fst snd) = Sigma.new fst ?
Dist_not_exists
Prove that "p is true for all x" implies "there is no x for which p is not true"
Dist_not_exists <a> <p: a -> Type> (f: (x: a) -> (p x)) : Not (Sigma a (x => ( Not (p x))))
Dist_not_exists a p f = ?
Dist_exists_or
Prove that existential quantification distributes over disjunction.
Dist_exists_or <a> <p: a -> Type> <q: a -> Type> : Equivalence (Sigma a (x => (Either (p x) (q x)))) (Either (Sigma a (x => (p x))) (Sigma a (x => (q x))))
Dist_exists_or a p q = ?dist_exists_or_rhs