module Data.Bool where
The booleans🔗
open import Data.Bool.Base public
Pattern matching on only the first argument might seem like a somewhat impractical 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:
: (x y z : Bool) → and x (and y z) ≡ and (and x y) z
and-associative = refl
and-associative false y z = refl
and-associative true y z
: (x y z : Bool) → or x (or y z) ≡ or (or x y) z
or-associative = refl
or-associative false y z = refl
or-associative true y z
: (x y : Bool) → and x y ≡ and y x
and-commutative = refl
and-commutative false false = refl
and-commutative false true = refl
and-commutative true false = refl
and-commutative true true
: (x y : Bool) → or x y ≡ or y x
or-commutative = refl
or-commutative false false = refl
or-commutative false true = refl
or-commutative true false = refl
or-commutative true true
: (x : Bool) → and x true ≡ x
and-truer = refl
and-truer false = refl
and-truer true
: (x : Bool) → and x false ≡ false
and-falser = refl
and-falser false = refl
and-falser true
: (x : Bool) → and true x ≡ x
and-truel = refl
and-truel x
: (x : Bool) → or x false ≡ x
or-falser = refl
or-falser false = refl
or-falser true
: (x : Bool) → or x true ≡ true
or-truer = refl
or-truer false = refl
or-truer true
: (x : Bool) → or false x ≡ x
or-falsel = refl
or-falsel x
: (x y : Bool) → and x (or x y) ≡ x
and-absorbs-orr = refl
and-absorbs-orr false y = refl
and-absorbs-orr true y
: (x y : Bool) → or x (and x y) ≡ x
or-absorbs-andr = refl
or-absorbs-andr false y = refl
or-absorbs-andr true y
: (x y z : Bool) → and x (or y z) ≡ or (and x y) (and x z)
and-distrib-orl = refl
and-distrib-orl false y z = refl
and-distrib-orl true y z
: (x y z : Bool) → or x (and y z) ≡ and (or x y) (or x z)
or-distrib-andl = refl
or-distrib-andl false y z = refl
or-distrib-andl true y z
: (x : Bool) → and x x ≡ x
and-idempotent = refl
and-idempotent false = refl
and-idempotent true
: (x : Bool) → or x x ≡ x
or-idempotent = refl
or-idempotent false = refl
or-idempotent true
: (x y z : Bool) → and (or x y) z ≡ or (and x z) (and y z)
and-distrib-orr =
and-distrib-orr true y z (or-absorbs-andr z y) ∙ ap (or z) (and-commutative z y)
sym = refl
and-distrib-orr false y z
: (x y z : Bool) → or (and x y) z ≡ and (or x z) (or y z)
or-distrib-andr = refl
or-distrib-andr true y z =
or-distrib-andr false y z (and-absorbs-orr z y) ∙ ap (and z) (or-commutative z y) sym
: ∀ {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-l
: ∀ {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
and-reflect-true-r
: ∀ {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-true
: ∀ {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-l
: ∀ {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
or-reflect-false-r
: ∀ {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 and-reflect-false
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:
: (x y : Bool) → not (and x y) ≡ or (not x) (not y)
not-and≡or-not = refl
not-and≡or-not false y = refl
not-and≡or-not true y
: (x y : Bool) → not (or x y) ≡ and (not x) (not y)
not-or≡and-not = refl
not-or≡and-not false y = refl
not-or≡and-not true y
: (x : Bool) → or (not x) x ≡ true
or-complementl = refl
or-complementl false = refl
or-complementl true
: (x : Bool) → and (not x) x ≡ false
and-complementl = refl
and-complementl false = refl and-complementl true
Furthermore, note that not
has
no fixed points.
: ∀ {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)) not-no-fixed
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:
: Bool → Bool → Bool
xor = y
xor false y = not y
xor true y
: (x y z : Bool) → xor x (xor y z) ≡ xor (xor x y) z
xor-associative = refl
xor-associative false y z = refl
xor-associative true false z = not-involutive z
xor-associative true true z
: (x y : Bool) → xor x y ≡ xor y x
xor-commutative = refl
xor-commutative false false = refl
xor-commutative false true = refl
xor-commutative true false = refl
xor-commutative true true
: (x : Bool) → xor x false ≡ x
xor-falser = refl
xor-falser false = refl
xor-falser true
: (x : Bool) → xor x true ≡ not x
xor-truer = refl
xor-truer false = refl
xor-truer true
: (x : Bool) → xor x x ≡ false
xor-inverse-self = refl
xor-inverse-self false = refl
xor-inverse-self true
: (x y z : Bool) → and (xor x y) z ≡ xor (and x z) (and y z)
and-distrib-xorr = refl
and-distrib-xorr false y z = and-falser (not y) ∙ sym (and-falser y)
and-distrib-xorr true y false = (and-truer (not y)) ∙ ap not (sym (and-truer y)) and-distrib-xorr true y true
Material implication between booleans also interacts nicely with many of the other operations:
: Bool → Bool → Bool
imp = true
imp false y = y
imp true y
: (x : Bool) → imp x true ≡ true
imp-truer = refl
imp-truer false = refl imp-truer true
Furthermore, material implication is equivalent to the classical definition.
: ∀ x y → or (not x) y ≡ imp x y
imp-not-or = refl
imp-not-or false y = refl imp-not-or true y
: ∀ {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
not-inj
: ∀ {x y} → x ≠ y → x ≡ not y
ne→is-not {true} {true} p = absurd (p refl)
ne→is-not {true} {false} p = refl
ne→is-not {false} {true} p = refl
ne→is-not {false} {false} p = absurd (p refl) ne→is-not
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.
module _ (e : Bool ≃ Bool) where
private module e = Equiv e
: ∀ x y → e.to x ≡ x → e.to y ≡ y
bool-equiv-id = α
bool-equiv-id true true α = α
bool-equiv-id false false α with e.to false in β
bool-equiv-id true false α ... | false = refl
... | true = absurd (true≠false (e.injective₂ α (Id≃path.to β)))
with e.to true in β
bool-equiv-id false true α ... | false = absurd (false≠true (e.injective₂ α (Id≃path.to β)))
... | true = refl
: ∀ x y → e.to x ≡ not x → e.to y ≡ not y
bool-equiv-not = α
bool-equiv-not true true α = α
bool-equiv-not false false α with e.to false in β
bool-equiv-not true false α ... | true = refl
... | false = absurd (true≠false (e.injective₂ α (Id≃path.to β)))
with e.to true in β
bool-equiv-not false true α ... | false = refl
... | true = absurd (false≠true (e.injective₂ α (Id≃path.to β)))
: ∀ x y → e.to x ≠ x → e.to y ≡ not y
bool-equiv-not' = bool-equiv-not x y (ne→is-not α) bool-equiv-not' x y α
private
: Bool ≃ Bool → Bool
classify with e .fst true ≡? true
classify e ... | yes _ = true
... | no _ = false
: Bool → Bool ≃ Bool
named = if_then id≃ else not≃
named
: (x : Bool) → classify (named x) ≡ x
classify-named = refl
classify-named true = refl
classify-named false
: (e : Bool ≃ Bool) → ∀ x → named (classify e) .fst x ≡ e .fst x
named-classify with e .fst true ≡? true
named-classify e x ... | yes p = sym (bool-equiv-id e true x p)
... | no ¬p = sym (bool-equiv-not e true x (ne→is-not ¬p))
: (Bool ≃ Bool) ≃ Bool
Bool-automorphisms .fst = classify
Bool-automorphisms .snd = is-iso→is-equiv record
Bool-automorphisms { from = named
; rinv = classify-named
; linv = λ e → Σ-pathp (funext (named-classify e))
(is-prop→pathp (λ _ → is-equiv-is-prop _) _ _)
}
: ∀ {ℓ} (P : Bool ≃ Bool → Type ℓ) → P id≃ → P not≃ → ∀ e → P e
Bool-equiv-elim with inspect (e .fst true)
Bool-equiv-elim P pid pnot e ... | true , p = subst P (ext λ x → sym (bool-equiv-id e true x p)) pid
... | false , p = subst P (ext λ x → sym (bool-equiv-not e true x p)) pnot