module Data.Bool where
The booleansπ
open import Data.Bool.Base public
Pattern matching on only the first argument might seem like a somewhat inpractical choice due to its asymmetry - however, it shortens a lot of proofs since we get a lot of judgemental equalities for free. For example, see the following statements:
and-associative : (x y z : Bool) β and x (and y z) β‘ and (and x y) z and-associative false y z = refl and-associative true y z = refl or-associative : (x y z : Bool) β or x (or y z) β‘ or (or x y) z or-associative false y z = refl or-associative true y z = refl and-commutative : (x y : Bool) β and x y β‘ and y x and-commutative false false = refl and-commutative false true = refl and-commutative true false = refl and-commutative true true = refl or-commutative : (x y : Bool) β or x y β‘ or y x or-commutative false false = refl or-commutative false true = refl or-commutative true false = refl or-commutative true true = refl and-truer : (x : Bool) β and x true β‘ x and-truer false = refl and-truer true = refl and-falser : (x : Bool) β and x false β‘ false and-falser false = refl and-falser true = refl and-truel : (x : Bool) β and true x β‘ x and-truel x = refl or-falser : (x : Bool) β or x false β‘ x or-falser false = refl or-falser true = refl or-truer : (x : Bool) β or x true β‘ true or-truer false = refl or-truer true = refl or-falsel : (x : Bool) β or false x β‘ x or-falsel x = refl and-absorbs-orr : (x y : Bool) β and x (or x y) β‘ x and-absorbs-orr false y = refl and-absorbs-orr true y = refl or-absorbs-andr : (x y : Bool) β or x (and x y) β‘ x or-absorbs-andr false y = refl or-absorbs-andr true y = refl and-distrib-orl : (x y z : Bool) β and x (or y z) β‘ or (and x y) (and x z) and-distrib-orl false y z = refl and-distrib-orl true y z = refl or-distrib-andl : (x y z : Bool) β or x (and y z) β‘ and (or x y) (or x z) or-distrib-andl false y z = refl or-distrib-andl true y z = refl and-idempotent : (x : Bool) β and x x β‘ x and-idempotent false = refl and-idempotent true = refl or-idempotent : (x : Bool) β or x x β‘ x or-idempotent false = refl or-idempotent true = refl and-distrib-orr : (x y z : Bool) β and (or x y) z β‘ or (and x z) (and y z) and-distrib-orr true y z = sym (or-absorbs-andr z y) β ap (or z) (and-commutative z y) and-distrib-orr false y z = refl or-distrib-andr : (x y z : Bool) β or (and x y) z β‘ and (or x z) (or y z) or-distrib-andr true y z = refl or-distrib-andr false y z = sym (and-absorbs-orr z y) β ap (and z) (or-commutative z y)
and-reflect-true-l : β {x y} β and x y β‘ true β x β‘ true and-reflect-true-l {x = true} p = refl and-reflect-true-l {x = false} p = p and-reflect-true-r : β {x y} β and x y β‘ true β y β‘ true and-reflect-true-r {x = true} {y = true} p = refl and-reflect-true-r {x = false} {y = true} p = refl and-reflect-true-r {x = true} {y = false} p = p and-reflect-true-r {x = false} {y = false} p = p or-reflect-true : β {x y} β or x y β‘ true β x β‘ true β y β‘ true or-reflect-true {x = true} {y = y} p = inl refl or-reflect-true {x = false} {y = true} p = inr refl or-reflect-true {x = false} {y = false} p = absurd (trueβ false (sym p)) or-reflect-false-l : β {x y} β or x y β‘ false β x β‘ false or-reflect-false-l {x = true} p = absurd (trueβ false p) or-reflect-false-l {x = false} p = refl or-reflect-false-r : β {x y} β or x y β‘ false β y β‘ false or-reflect-false-r {x = true} {y = true} p = absurd (trueβ false p) or-reflect-false-r {x = true} {y = false} p = refl or-reflect-false-r {x = false} {y = true} p = absurd (trueβ false p) or-reflect-false-r {x = false} {y = false} p = refl and-reflect-false : β {x y} β and x y β‘ false β x β‘ false β y β‘ false and-reflect-false {x = true} {y = y} p = inr p and-reflect-false {x = false} {y = y} p = inl refl
All the properties above hold both in classical and constructive mathematics, even in minimal logic that fails to validate both the law of the excluded middle as well as the law of noncontradiction. However, the boolean operations satisfy both of these laws:
not-involutive : (x : Bool) β not (not x) β‘ x not-involutive false i = false not-involutive true i = true not-andβ‘or-not : (x y : Bool) β not (and x y) β‘ or (not x) (not y) not-andβ‘or-not false y = refl not-andβ‘or-not true y = refl not-orβ‘and-not : (x y : Bool) β not (or x y) β‘ and (not x) (not y) not-orβ‘and-not false y = refl not-orβ‘and-not true y = refl or-complementl : (x : Bool) β or (not x) x β‘ true or-complementl false = refl or-complementl true = refl and-complementl : (x : Bool) β and (not x) x β‘ false and-complementl false = refl and-complementl true = refl
Furthermore, note that not
has no fixed points.
not-no-fixed : β {x} β x β‘ not x β β₯ not-no-fixed {x = true} p = absurd (trueβ false p) not-no-fixed {x = false} p = absurd (trueβ false (sym p))
Exclusive disjunction (usually known as XOR) also yields additional structure - in particular, it can be viewed as an addition operator in a ring whose multiplication is defined by conjunction:
xor : Bool β Bool β Bool xor false y = y xor true y = not y xor-associative : (x y z : Bool) β xor x (xor y z) β‘ xor (xor x y) z xor-associative false y z = refl xor-associative true false z = refl xor-associative true true z = not-involutive z xor-commutative : (x y : Bool) β xor x y β‘ xor y x xor-commutative false false = refl xor-commutative false true = refl xor-commutative true false = refl xor-commutative true true = refl xor-falser : (x : Bool) β xor x false β‘ x xor-falser false = refl xor-falser true = refl xor-truer : (x : Bool) β xor x true β‘ not x xor-truer false = refl xor-truer true = refl xor-inverse-self : (x : Bool) β xor x x β‘ false xor-inverse-self false = refl xor-inverse-self true = refl and-distrib-xorr : (x y z : Bool) β and (xor x y) z β‘ xor (and x z) (and y z) and-distrib-xorr false y z = refl and-distrib-xorr true y false = and-falser (not y) β sym (and-falser y) and-distrib-xorr true y true = (and-truer (not y)) β ap not (sym (and-truer y))
Material implication between booleans also interacts nicely with many of the other operations:
imp : Bool β Bool β Bool imp false y = true imp true y = y imp-truer : (x : Bool) β imp x true β‘ true imp-truer false = refl imp-truer true = refl
Furthermore, material implication is equivalent to the classical definition.
imp-not-or : β x y β or (not x) y β‘ imp x y imp-not-or false y = refl imp-not-or true y = refl
The βnotβ equivalenceπ
The construction of not
as an equivalence factors
through showing that not
is an isomorphism. In particular,
not
is its own inverse, so we
need a proof that itβs involutive, as is proven in not-involutive
. With this, we
can get a proof that itβs an equivalence:
not-is-equiv : is-equiv not not-is-equiv = is-involutiveβis-equiv not-involutive
not-inj : β {x y} β not x β‘ not y β x β‘ y not-inj {x = true} {y = true} p = refl not-inj {x = true} {y = false} p = sym p not-inj {x = false} {y = true} p = sym p not-inj {x = false} {y = false} p = refl
Aut(Bool)π
We characterise the type Bool β‘ Bool
. There are exactly
two paths, and we can decide which path weβre looking at by seeing how
it acts on the element true
.
First, two small lemmas: we can tell whether weβre looking at the identity equivalence or the βnotβ equivalence by seeing how it acts on the constructors.
private idLemma : (p : Bool β Bool) β p .fst true β‘ true β p .fst false β‘ false β p β‘ (_ , id-equiv) idLemma p p1 p2 = Ξ£-path (funext lemma) (is-equiv-is-prop _ _ _) where lemma : (x : Bool) β _ lemma false = p2 lemma true = p1
If it quacks like the identity equivalence, then it must be.
Otherwise weβre looking at the not
equivalence.
notLemma : (p : Bool β Bool) β p .fst true β‘ false β p .fst false β‘ true β p β‘ (not , not-is-equiv) notLemma p p1 p2 = Ξ£-path (funext lemma) (is-equiv-is-prop _ _ _) where lemma : (x : Bool) β _ lemma false = p2 lemma true = p1
With these two lemmas, we can proceed to classify the automorphisms
of Bool
. For this, weβll need
another lemma: If a function Bool β Bool
doesnβt
map f x β‘ x
, then it maps f x β‘ not x
.
Bool-autβ‘2 : (Bool β‘ Bool) β‘ Lift _ Bool Bool-autβ‘2 = IsoβPath the-iso where lemma : (f : Bool β Bool) {x : Bool} β Β¬ f x β‘ x β f x β‘ not x lemma f {false} x = caseα΅ (f false β‘ true) of Ξ» where (yes p) β p (no Β¬p) β absurd (Β¬p (xβ falseβxβ‘true _ x)) lemma f {true} x = caseα΅ (f true β‘ false) of Ξ» where (yes p) β p (no Β¬p) β absurd (Β¬p (xβ trueβxβ‘false _ x))
This lemma is slightly annoying to prove, but itβs not too
complicated. Itβs essentially two case splits: first on the boolean, and
second on whether weβre looking at f x β‘ not x
. If we are,
then itβs fine (those are the yes p = p
cases) - otherwise
that contradicts what weβve been told.
the-iso : Iso (Bool β‘ Bool) (Lift _ Bool) fst the-iso path = caseα΅ (transport path true β‘ true) of Ξ» where (yes path) β lift false (no Β¬path) β lift true
Now we classify the isomorphism by looking at what it does to true
. We
arbitrarily map refl
to false
and not
to true
.
the-iso .snd .is-iso.inv (lift false) = refl the-iso .snd .is-iso.inv (lift true) = ua (not , not-is-equiv)
The inverse is determined by the same rule, but backwards. Thatβs why
itβs an inverse! Everything computes in a way that lines up to this
function being a right inverse
on the nose.
the-iso .snd .is-iso.rinv (lift false) = refl the-iso .snd .is-iso.rinv (lift true) = refl
The left inverse is a lot more complicated to prove. We examine how
the path acts on both true
and false
. There
are four cases:
the-iso .snd .is-iso.linv path with transport path true β‘? true | transport path false β‘? false ... | yes trueβtrue | yes falseβfalse = refl β‘β¨ sym (PathβEquiv .snd .linv _) β©β‘ ua (pathβequiv refl) β‘β¨ ap ua pathβequiv-refl β©β‘ ua (_ , id-equiv) β‘β¨ ap ua (sym (idLemma _ trueβtrue falseβfalse)) β©β‘ ua (pathβequiv path) β‘β¨ PathβEquiv .snd .linv _ β©β‘ path β
In the case where the path quacks like reflexivity, we use the univalence axiom to show that
we must be looking at the reflexivity path. For this, we use
idLemma
to show that pathβequiv path
must be
the identity equivalence.
... | yes trueβtrue | no falseβtrue' = let falseβtrue = lemma (transport path) falseβtrue' fibres = is-contrβis-prop (pathβequiv path .snd .is-eqv true) (true , trueβtrue) (false , falseβtrue) in absurd (trueβ false (ap fst fibres))
The second case is when both booleans map to true
. This is a
contradiction - transport along a path is an equivalence, and
equivalences have contractible fibres; Since we have two fibres over
true
, that means we
must have true β‘ false
.
... | no trueβfalse' | yes falseβfalse = let trueβfalse = lemma (transport path) trueβfalse' fibres = is-contrβis-prop (pathβequiv path .snd .is-eqv false) (true , trueβfalse) (false , falseβfalse) in absurd (trueβ false (ap fst fibres))
The other case is analogous.
... | no trueβfalse' | no falseβtrue' = ua (not , not-is-equiv) β‘β¨ ap ua (sym (notLemma _ (lemma (transport path) trueβfalse') (lemma (transport path) falseβtrue'))) β©β‘ ua (pathβequiv path) β‘β¨ PathβEquiv .snd .linv _ β©β‘ path β
The last case is when the path quacks like ua (not, _)
-
in that case, we use the notLemma
to show it
must be ua (not, _)
, and the univalence axiom
finishes the job.