module Logic.Propositional.Classical where

Classical propositional logicπŸ”—

Classical propositional logic is a simple classical logic that only contains β€œatomic” propositions and connectives like β€œand”, β€œor” and β€œnot”. Notably, it does not contain any quantifiers like β€œforall” and β€œexists”.

We can define the syntax of classical propositional logic in Agda as the following indexed inductive type.

data Proposition (Ξ“ : Nat) : Type where
  atom        : Fin Ξ“ β†’ Proposition Ξ“
  β€œβŠ€β€ β€œβŠ₯”     : Proposition Ξ“
  _β€œβˆ§β€_ _β€œβˆ¨β€_ : Proposition Ξ“ β†’ Proposition Ξ“ β†’ Proposition Ξ“
  β€œΒ¬β€_        : Proposition Ξ“ β†’ Proposition Ξ“

We represent atomic propositions as elements of Fin, and our indexing scheme gives an upper bound on the number of atomic propositions.

Next, we introduce contexts; these are simply lists of propositions.

data Ctx (Ξ“ : Nat) : Type where
  []  : Ctx Ξ“
  _β¨Ύ_ : Ctx Ξ“ β†’ Proposition Ξ“ β†’ Ctx Ξ“

_++_ : βˆ€ {Ξ“} β†’ Ctx Ξ“ β†’ Ctx Ξ“ β†’ Ctx Ξ“
ψ ++ [] = ψ
ψ ++ (θ ⨾ P) = (ψ ++ θ) ⨾ P

We also define a membership relation for contexts.

data _∈ᢜ_ {Ξ“ : Nat} (P : Proposition Ξ“) : Ctx Ξ“ β†’ Type where
  here  : βˆ€ {ψ} β†’ P ∈ᢜ (ψ β¨Ύ P)
  there : βˆ€ {ψ Q} β†’ P ∈ᢜ ψ β†’ P ∈ᢜ (ψ β¨Ύ Q)

Finally, we define a natural deduction proof calculus for classical propositional logic. Our proof systems is encoded as an indexed inductive _⊒_; elements of ψ ⊒ P are proof trees that show P from some set of hypotheses ψ.

data _⊒_ {Ξ“ : Nat} (ψ : Ctx Ξ“) : Proposition Ξ“ β†’ Type where

Our first rule is pretty intuitive: If P is in our hypotheses ψ, then we can deduce P from ψ.

  hyp : βˆ€ {P} β†’ P ∈ ψ β†’ ψ ⊒ P

Next, we give an introduction rule for β€œtrue” and an elimination rule for β€œfalse”.

  ⊀-intro : ψ ⊒ β€œβŠ€β€
  βŠ₯-elim  : βˆ€ {P} β†’ ψ ⊒ β€œβŠ₯” β†’ ψ ⊒ P

The rules for β€œand” are more or less what one would expect.

  ∧-intro  : βˆ€ {P Q} β†’ ψ ⊒ P β†’ ψ ⊒ Q β†’ ψ ⊒ P β€œβˆ§β€ Q
  ∧-elim-l : βˆ€ {P Q} β†’ ψ ⊒ P β€œβˆ§β€ Q β†’ ψ ⊒ P
  ∧-elim-r : βˆ€ {P Q} β†’ ψ ⊒ P β€œβˆ§β€ Q β†’ ψ ⊒ Q

However, β€œor” is a bit trickier, as we need to add things to our hypotheses in the elimination rule.

  ∨-intro-l : βˆ€ {P Q}   β†’ ψ ⊒ P β†’ ψ ⊒ P β€œβˆ¨β€ Q
  ∨-intro-r : βˆ€ {P Q}   β†’ ψ ⊒ Q β†’ ψ ⊒ P β€œβˆ¨β€ Q
  ∨-elim    : βˆ€ {P Q R} β†’ ψ ⊒ P β€œβˆ¨β€ Q β†’ ψ β¨Ύ P ⊒ R β†’ ψ β¨Ύ Q ⊒ R β†’ ψ ⊒ R

Finally, we add the rules for negation. To ensure that we are working with classical logic, we add a rule for double-negation elimination.

  Β¬-intro   : βˆ€ {P} β†’ ψ β¨Ύ P ⊒ β€œβŠ₯” β†’ ψ ⊒ β€œΒ¬β€ P
  Β¬-elim    : βˆ€ {P} β†’ ψ ⊒ β€œΒ¬β€ P β†’ ψ ⊒ P β†’ ψ ⊒ β€œβŠ₯”
  dneg-elim : βˆ€ {P} β†’ ψ ⊒ β€œΒ¬β€ (β€œΒ¬β€ P) β†’ ψ ⊒ P

WeakeningπŸ”—

Indexing the type of propositions by the number of atomic propositions makes our life easier later. However, it does mean that we need to define weakening, which lets us increase the upper bound on the number of atoms in a proposition.

data Weakening (Ξ“ : Nat) : Nat β†’ Type where
  done : Weakening Ξ“ Ξ“
  weak : βˆ€ {Ξ”} β†’ Weakening (suc Ξ“) Ξ” β†’ Weakening Ξ“ Ξ”
We omit the implementation of applying weakenings to the syntactic types, since it’s pretty much mechanical.
Β¬weaken-suc-zero : Weakening (suc Ξ“) 0 β†’ βŠ₯
Β¬weaken-suc-zero (weak Οƒ) = Β¬weaken-suc-zero Οƒ

wk-suc : Weakening Ξ“ Ξ” β†’ Weakening Ξ“ (suc Ξ”)
wk-suc done     = weak done
wk-suc (weak Οƒ) = weak (wk-suc Οƒ)

!wk : Weakening 0 Ξ“
!wk {Ξ“ = zero}  = done
!wk {Ξ“ = suc Ξ“} = wk-suc !wk

inc-prop : Proposition Ξ“ β†’ Proposition (suc Ξ“)
inc-prop (atom x)  = atom (weaken x)
inc-prop β€œβŠ€β€       = β€œβŠ€β€
inc-prop β€œβŠ₯”       = β€œβŠ₯”
inc-prop (p β€œβˆ§β€ q) = inc-prop p β€œβˆ§β€ inc-prop q
inc-prop (p β€œβˆ¨β€ q) = inc-prop p β€œβˆ¨β€ inc-prop q
inc-prop (β€œΒ¬β€ p)   = β€œΒ¬β€ (inc-prop p)

inc-ctx : Ctx Ξ“ β†’ Ctx (suc Ξ“)
inc-ctx []      = []
inc-ctx (ψ ⨾ P) = inc-ctx ψ ⨾ inc-prop P

inc-atom : P ∈ ψ β†’ inc-prop P ∈ᢜ inc-ctx ψ
inc-atom here      = here
inc-atom (there x) = there (inc-atom x)

inc-proof : ψ ⊒ P β†’ inc-ctx ψ ⊒ inc-prop P
inc-proof (hyp x)        = hyp (inc-atom  x)
inc-proof ⊀-intro        = ⊀-intro
inc-proof (βŠ₯-elim p)     = βŠ₯-elim (inc-proof p)
inc-proof (∧-intro p q)  = ∧-intro (inc-proof p) (inc-proof q)
inc-proof (∧-elim-l p)   = ∧-elim-l (inc-proof p)
inc-proof (∧-elim-r p)   = ∧-elim-r (inc-proof p)
inc-proof (∨-intro-l p)  = ∨-intro-l (inc-proof p)
inc-proof (∨-intro-r p)  = ∨-intro-r (inc-proof p)
inc-proof (∨-elim p q r) = ∨-elim (inc-proof p) (inc-proof q) (inc-proof r)
inc-proof (Β¬-intro p)    = Β¬-intro (inc-proof p)
inc-proof (Β¬-elim p q)   = Β¬-elim (inc-proof p) (inc-proof q)
inc-proof (dneg-elim p)  = dneg-elim (inc-proof p)

bump-prop : Proposition Ξ“ β†’ Proposition (suc Ξ“)
bump-prop (atom x)  = atom (fsuc x)
bump-prop β€œβŠ€β€       = β€œβŠ€β€
bump-prop β€œβŠ₯”       = β€œβŠ₯”
bump-prop (p β€œβˆ§β€ q) = bump-prop p β€œβˆ§β€ bump-prop q
bump-prop (p β€œβˆ¨β€ q) = bump-prop p β€œβˆ¨β€ bump-prop q
bump-prop (β€œΒ¬β€ p)   = β€œΒ¬β€ bump-prop p

bump-ctx : Ctx Ξ“ β†’ Ctx (suc Ξ“)
bump-ctx []      = []
bump-ctx (ψ ⨾ P) = bump-ctx ψ ⨾ bump-prop P

bump-atom : P ∈ ψ β†’ bump-prop P ∈ᢜ bump-ctx ψ
bump-atom here      = here
bump-atom (there p) = there (bump-atom p)

bump-proof : ψ ⊒ P β†’ bump-ctx ψ ⊒ bump-prop P
bump-proof (hyp x)        = hyp (bump-atom x)
bump-proof ⊀-intro        = ⊀-intro
bump-proof (βŠ₯-elim p)     = βŠ₯-elim (bump-proof p)
bump-proof (∧-intro p q)  = ∧-intro (bump-proof p) (bump-proof q)
bump-proof (∧-elim-l p)   = ∧-elim-l (bump-proof p)
bump-proof (∧-elim-r p)   = ∧-elim-r (bump-proof p)
bump-proof (∨-intro-l p)  = ∨-intro-l (bump-proof p)
bump-proof (∨-intro-r p)  = ∨-intro-r (bump-proof p)
bump-proof (∨-elim p q r) = ∨-elim (bump-proof p) (bump-proof q) (bump-proof r)
bump-proof (Β¬-intro p)    = Β¬-intro (bump-proof p)
bump-proof (Β¬-elim p q)   = Β¬-elim (bump-proof p) (bump-proof q)
bump-proof (dneg-elim p)  = dneg-elim (bump-proof p)

wk-atom : Weakening Ξ“ Ξ” β†’ Fin Ξ“ β†’ Fin Ξ”
wk-atom done     x = x
wk-atom (weak Οƒ) x = wk-atom Οƒ (weaken x)

wk-prop : Weakening Ξ“ Ξ” β†’ Proposition Ξ“ β†’ Proposition Ξ”
wk-prop done     P = P
wk-prop (weak Οƒ) P = wk-prop Οƒ (inc-prop P)

wk-ctx : Weakening Ξ“ Ξ” β†’ Ctx Ξ“ β†’ Ctx Ξ”
wk-ctx done     ψ = ψ
wk-ctx (weak Οƒ) ψ = wk-ctx Οƒ (inc-ctx ψ)

wk-proof : (Οƒ : Weakening Ξ“ Ξ”) β†’ ψ ⊒ P β†’ wk-ctx Οƒ ψ ⊒ wk-prop Οƒ P
wk-proof done     pf = pf
wk-proof (weak Οƒ) pf = wk-proof Οƒ (inc-proof pf)

shift-atom : Weakening Ξ“ Ξ” β†’ Fin Ξ“ β†’ Fin Ξ”
shift-atom done     i = i
shift-atom (weak Οƒ) i = shift-atom Οƒ (fsuc i)

shift-prop : Weakening Ξ“ Ξ” β†’ Proposition Ξ“ β†’ Proposition Ξ”
shift-prop done     p = p
shift-prop (weak Οƒ) p = shift-prop Οƒ (bump-prop p)

shift-ctx : Weakening Ξ“ Ξ” β†’ Ctx Ξ“ β†’ Ctx Ξ”
shift-ctx done     ψ = ψ
shift-ctx (weak Οƒ) ψ = shift-ctx Οƒ (bump-ctx ψ)

shift-ctx-[]
  : (Οƒ : Weakening Ξ“ Ξ”)
  β†’ shift-ctx Οƒ [] ≑ []
shift-ctx-[] done = refl
shift-ctx-[] (weak Οƒ) = shift-ctx-[] Οƒ

shift-ctx-β¨Ύ
  : (Οƒ : Weakening Ξ“ Ξ”)
  β†’ (ψ : Ctx Ξ“) (P : Proposition Ξ“)
  β†’ shift-ctx Οƒ (ψ β¨Ύ P) ≑ shift-ctx Οƒ ψ β¨Ύ shift-prop Οƒ P
shift-ctx-⨾ done     ψ P = refl
shift-ctx-β¨Ύ (weak Οƒ) ψ P = shift-ctx-β¨Ύ Οƒ (bump-ctx ψ) (bump-prop P)

shift-prop-β€œΒ¬β€
  : (Οƒ : Weakening Ξ“ Ξ”)
  β†’ (P : Proposition Ξ“)
  β†’ shift-prop Οƒ (β€œΒ¬β€ P) ≑ β€œΒ¬β€ (shift-prop Οƒ P)
shift-prop-β€œΒ¬β€ done     P = refl
shift-prop-β€œΒ¬β€ (weak Οƒ) P = shift-prop-β€œΒ¬β€ Οƒ (bump-prop P)

Context inclusionsπŸ”—

We also want to be able to modify our collection of hypotheses. We do this by defining a notion of context inclusion, where an inclusion denotes that we can obtain every hypothesis in by dropping hypotheses from We call such context inclusions renamings.

data Rename {Ξ“} : Ctx Ξ“ β†’ Ctx Ξ“ β†’ Type where
  done : Rename [] []
  drop : βˆ€ {ψ ΞΈ P} β†’ Rename ψ ΞΈ β†’ Rename (ψ β¨Ύ P) ΞΈ
  keep : βˆ€ {ψ ΞΈ P} β†’ Rename ψ ΞΈ β†’ Rename (ψ β¨Ύ P) (ΞΈ β¨Ύ P)

Identity renamings exist, and renamings are closed under composition. With a bit of elbow grease, we could show that contexts and renamings form a precategory, thought we do not do so here.

idrn : Rename ψ ψ
idrn {ψ = []} = done
idrn {ψ = ψ ⨾ P} = keep (idrn {ψ = ψ})

_∘rn_ : Rename ΞΈ ΞΆ β†’ Rename ψ ΞΈ β†’ Rename ψ ΞΆ
p      ∘rn done   = p
p      ∘rn drop q = drop (p ∘rn q)
drop p ∘rn keep q = drop (p ∘rn q)
keep p ∘rn keep q = keep (p ∘rn q)

We also have a renaming that drops every hypothesis, and renamings that project out half of a context. Categorically, these correspond to the maps into the terminal object and maps out of the product, resp.

!rn : Rename ψ []
!rn {ψ = []}    = done
!rn {ψ = ψ ⨾ x} = drop !rn

π₁-rn : Rename (ψ ++ ΞΈ) ψ
π₁-rn {ΞΈ = []}    = idrn
π₁-rn {ΞΈ = ΞΈ β¨Ύ P} = drop π₁-rn

Ο€β‚‚-rn : Rename (ψ ++ ΞΈ) ΞΈ
Ο€β‚‚-rn {ΞΈ = []}    = !rn
Ο€β‚‚-rn {ΞΈ = ΞΈ β¨Ύ P} = keep Ο€β‚‚-rn

Renamings act contravariantly on variables and derivations. In categorical terms, this induces a presheaf on the category of contexts.

rename-hyp : Rename ψ ΞΈ β†’ P ∈ᢜ ΞΈ β†’ P ∈ᢜ ψ
rename-hyp (drop rn) mem         = there (rename-hyp rn mem)
rename-hyp (keep rn) here        = here
rename-hyp (keep rn) (there mem) = there (rename-hyp rn mem)

rename : Rename ψ ΞΈ β†’ ΞΈ ⊒ P β†’ ψ ⊒ P
rename rn (hyp x)        = hyp (rename-hyp rn x)
rename rn ⊀-intro        = ⊀-intro
rename rn (βŠ₯-elim p)     = βŠ₯-elim (rename rn p)
rename rn (∧-intro p q)  = ∧-intro (rename rn p) (rename rn q)
rename rn (∧-elim-l p)   = ∧-elim-l (rename rn p)
rename rn (∧-elim-r p)   = ∧-elim-r (rename rn p)
rename rn (∨-intro-l p)  = ∨-intro-l (rename rn p)
rename rn (∨-intro-r p)  = ∨-intro-r (rename rn p)
rename rn (∨-elim p q r) = ∨-elim
  (rename rn p) (rename (keep rn) q) (rename (keep rn) r)
rename rn (Β¬-intro p)    = Β¬-intro (rename (keep rn) p)
rename rn (Β¬-elim p q)   = Β¬-elim (rename rn p) (rename rn q)
rename rn (dneg-elim p)  = dneg-elim (rename rn p)

Some elementary theoremsπŸ”—

With those bits of structural work completed, we can focus our attention to some theorems. First, note that we can prove the law of excluded middle.

lem : βˆ€ {Ξ“} {ψ : Ctx Ξ“} (P : Proposition Ξ“) β†’ ψ ⊒ P β€œβˆ¨β€ (β€œΒ¬β€ P)
lem P =
  dneg-elim                 $
  Β¬-intro                   $
  Β¬-elim (hyp here)         $
  ∨-intro-r                 $
  Β¬-intro                   $
  Β¬-elim (hyp (there here)) $
  ∨-intro-l (hyp here)

We also have all four De Morgan laws.

dneg-intro : ψ ⊒ P β†’ ψ ⊒ β€œΒ¬β€ (β€œΒ¬β€ P)
dneg-intro p = Β¬-intro (Β¬-elim (hyp here) (rename (drop idrn) p))

¬∧-intro-l : ψ ⊒ β€œΒ¬β€ P β†’ ψ ⊒ β€œΒ¬β€ (P β€œβˆ§β€ Q)
¬∧-intro-l p = ¬-intro (¬-elim (rename (drop idrn) p) (∧-elim-l (hyp here)))

¬∧-intro-r : ψ ⊒ β€œΒ¬β€ Q β†’ ψ ⊒ β€œΒ¬β€ (P β€œβˆ§β€ Q)
¬∧-intro-r p = ¬-intro (¬-elim (rename (drop idrn) p) (∧-elim-r (hyp here)))

¬∧-elim : ψ ⊒ β€œΒ¬β€ (P β€œβˆ§β€ Q) β†’ ψ ⊒ (β€œΒ¬β€ P) β€œβˆ¨β€ (β€œΒ¬β€ Q)
¬∧-elim {P = P} {Q = Q} p =
  ∨-elim (lem P)
    (∨-elim (lem Q)
      (βŠ₯-elim
        (Β¬-elim (rename (drop (drop idrn)) p)
        (∧-intro (hyp (there here)) (hyp here))))
      (∨-intro-r (hyp here)))
    (∨-intro-l (hyp here))

¬∨-intro : ψ ⊒ β€œΒ¬β€ P β†’ ψ ⊒ β€œΒ¬β€ Q β†’ ψ ⊒ β€œΒ¬β€ (P β€œβˆ¨β€ Q)
¬∨-intro p q =
  Β¬-intro $
  ∨-elim (hyp here)
    (Β¬-elim (rename (drop (drop idrn)) p) (hyp here))
    (Β¬-elim (rename (drop (drop idrn)) q) (hyp here))

As we are working in a classical setting, we can define implication P β‡’ Q as Β¬ P ∨ Q. Note that the normal introduction and elimination rules for implications are theorems.

opaque
  _β€œβ‡’β€_ : βˆ€ {Ξ“} β†’ Proposition Ξ“ β†’ Proposition Ξ“ β†’ Proposition Ξ“
  P β€œβ‡’β€ Q = (β€œΒ¬β€ P) β€œβˆ¨β€ Q

  β‡’-intro : ψ β¨Ύ P ⊒ Q β†’ ψ ⊒ P β€œβ‡’β€ Q
  β‡’-intro {P = P} pf = ∨-elim (lem P) (∨-intro-r pf) (∨-intro-l (hyp here))

  β‡’-elim : ψ ⊒ P β€œβ‡’β€ Q β†’ ψ ⊒ P β†’ ψ ⊒ Q
  β‡’-elim {Q = Q} imp p = ∨-elim imp
    (βŠ₯-elim (Β¬-elim (hyp here) (rename (drop idrn) p)))
    (hyp here)

We also prove some helpful extra lemmas regarding implications.

β‡’-uncurry : ψ ⊒ P β€œβ‡’β€ Q β†’ ψ β¨Ύ P ⊒ Q
β‡’-uncurry p = β‡’-elim (rename (drop idrn) p) (hyp here)

β‡’-flip : ψ ⊒ P β€œβ‡’β€ Q β€œβ‡’β€ R β†’ ψ ⊒ Q β€œβ‡’β€ P β€œβ‡’β€ R
β‡’-flip p =
  β‡’-intro $ β‡’-intro $
  β‡’-elim
    (β‡’-elim (rename (drop (drop idrn)) p) (hyp here))
    (hyp (there here))

Next, we define a conjunction operator that works on lists of propositions.

β€œβ‹€β€ : Ctx Ξ“ β†’ Proposition Ξ“
β€œβ‹€β€ []      = β€œβŠ€β€
β€œβ‹€β€ (ψ β¨Ύ P) = β€œβ‹€β€ ψ β€œβˆ§β€ P

β‹€-intro : (βˆ€ {P} β†’ P ∈ ψ β†’ ΞΈ ⊒ P) β†’ ΞΈ ⊒ β€œβ‹€β€ ψ
β‹€-intro {ψ = []}    pfs = ⊀-intro
β‹€-intro {ψ = ψ β¨Ύ P} pfs = ∧-intro (β‹€-intro (pfs ∘ there)) (pfs here)

β‹€-elim : P ∈ ψ β†’ ΞΈ ⊒ β€œβ‹€β€ ψ β†’ ΞΈ ⊒ P
β‹€-elim here      p = ∧-elim-r p
β‹€-elim (there x) p = β‹€-elim x (∧-elim-l p)

We can also define an n-ary implication connective.

_β€œβ‡›β€_ : Ctx Ξ“ β†’ Proposition Ξ“ β†’ Proposition Ξ“
[]      β€œβ‡›β€ P = P
(ψ β¨Ύ Q) β€œβ‡›β€ P = ψ β€œβ‡›β€ (Q β€œβ‡’β€ P)

⇛-intro : ΞΈ ++ ψ ⊒ P β†’ ΞΈ ⊒ ψ β€œβ‡›β€ P
⇛-intro {ψ = []}    p = p
⇛-intro {ψ = ψ β¨Ύ Q} p = ⇛-intro (β‡’-intro p)

⇛-uncurry : ΞΈ ⊒ ψ β€œβ‡›β€ P β†’ ΞΈ ++ ψ ⊒ P
⇛-uncurry {ψ = []}    p = p
⇛-uncurry {ψ = ψ β¨Ύ Q} p = β‡’-uncurry (⇛-uncurry p)

⇛-elim : ΞΈ ⊒ (ψ β¨Ύ P) β€œβ‡›β€ Q β†’ ΞΈ ⊒ P β†’ ΞΈ ⊒ ψ β€œβ‡›β€ Q
⇛-elim {ψ = ψ} p q = ⇛-intro (β‡’-elim (⇛-uncurry {ψ = ψ} p) (rename π₁-rn q))

SemanticsπŸ”—

The most obvious way to interpret classical logic is as operations on booleans.

sem-prop : βˆ€ {Ξ“} β†’ Proposition Ξ“ β†’ (Fin Ξ“ β†’ Bool) β†’ Bool
sem-prop (atom x)  ρ = ρ x
sem-prop β€œβŠ€β€       ρ = true
sem-prop β€œβŠ₯”       ρ = false
sem-prop (P β€œβˆ§β€ Q) ρ = and (sem-prop P ρ) (sem-prop Q ρ)
sem-prop (P β€œβˆ¨β€ Q) ρ = or (sem-prop P ρ) (sem-prop Q ρ)
sem-prop (β€œΒ¬β€ P)   ρ = not (sem-prop P ρ)

sem-ctx : βˆ€ {Ξ“} β†’ Ctx Ξ“ β†’ (Fin Ξ“ β†’ Bool) β†’ Bool
sem-ctx ψ ρ = sem-prop (β€œβ‹€β€ ψ) ρ

We also define a notion of semantic entailment, wherein ψ semantically entails P when for every assigment of variables ρ, if ⟦ ψ ⟧ ρ ≑ true, then ⟦ P ⟧ ρ ≑ true.

_⊨_ : βˆ€ {Ξ“} β†’ Ctx Ξ“ β†’ Proposition Ξ“ β†’ Type
_⊨_ {Ξ“} ψ P = βˆ€ (ρ : Fin Ξ“ β†’ Bool) β†’ ⟦ ψ ⟧ ρ ≑ true β†’ ⟦ P ⟧ ρ ≑ true

SoundnessπŸ”—

Soundness is something that is often poorly explained in classical logic.

The traditional definition goes something like this: a logic is sound if every provable theorem ψ ⊒ P is true. This definition obscures what is really going on: what we care about is that a provable theorem ψ ⊒ P is true in some semantics.

For classical propositional logic, it happens to be the case that we can focus our attention on one semantics (the booleans) β€” we’ll see ahead why this is the case β€” so authors often conflate β€œtrue” with β€œtrue in this particular semantics”.

The actual statement of soundness that we will be using is the following: if ψ ⊒ P is provable, then we have the semantic entailment ψ ⊨ P. This follows by induction on the proof trees, and it’s mostly mechanical.

hyp-sound : βˆ€ {Ξ“} {ψ : Ctx Ξ“} {P : Proposition Ξ“} β†’ P ∈ ψ β†’ ψ ⊨ P
hyp-sound here      ρ hyps-true = and-reflect-true-r hyps-true
hyp-sound (there m) ρ hyps-true = hyp-sound m ρ (and-reflect-true-l hyps-true)

sound : ψ ⊒ P β†’ ψ ⊨ P
sound (hyp x)    ρ hyps-true = hyp-sound x ρ hyps-true
sound ⊀-intro    ρ hyps-true = refl
sound (βŠ₯-elim p) ρ hyps-true =
  absurd (trueβ‰ false (sym (sound p ρ hyps-true)))

sound (∧-intro p q) ρ hyps-true =
  apβ‚‚ and (sound p ρ hyps-true) (sound q ρ hyps-true)
sound (∧-elim-l p) ρ hyps-true =
  and-reflect-true-l (sound p ρ hyps-true)
sound (∧-elim-r p) ρ hyps-true =
  and-reflect-true-r (sound p ρ hyps-true)

sound (∨-intro-l p) ρ hyps-true =
  apβ‚‚ or (sound p ρ hyps-true) refl
sound (∨-intro-r p) ρ hyps-true =
  apβ‚‚ or refl (sound p ρ hyps-true) βˆ™ or-truer _
sound (∨-elim p q r) ρ hyps-true with or-reflect-true (sound p ρ hyps-true)
... | inl P-true = sound q ρ (apβ‚‚ and hyps-true P-true)
... | inr Q-true = sound r ρ (apβ‚‚ and hyps-true Q-true)

sound (¬-intro {P = P} p) ρ hyps-true =
  Bool-elim (Ξ» b β†’ ⟦ P ⟧ ρ ≑ b β†’ not b ≑ true)
    (Ξ» P-true β†’ sound p ρ (apβ‚‚ and hyps-true P-true))
    (Ξ» _ β†’ refl)
    (⟦ P ⟧ ρ) refl
sound (¬-elim {P = P} p q) ρ hyps-true =
  absurd (not-no-fixed (sound q ρ hyps-true βˆ™ sym (sound p ρ hyps-true)))
sound (dneg-elim {P = P} p) ρ hyps-true =
  sym (not-involutive (⟦ P ⟧ ρ)) βˆ™ sound p ρ hyps-true

CompletenessπŸ”—

As mentioned earlier, we often think of classical propositional logic as having a single semantics. This is largely justified by the fact that if ψ semantically entails P in the booleans, then we can prove that ψ ⊒ P! In other words, the Boolean semantics for classical propositional logic is complete.

This somewhat miraculous theorem is often taken for granted. Completeness is why truth-tables are a valid form of reasoning, but this is hardly ever mentioned when teaching logic. As we shall soon see, this theorem isn’t exactly trivial, either.

First, let us lay out our general proof strategy. We shall start by building a context from an assignment that contains only (potentially negated) atoms. If assigns true to an atom we will add to Conversely, if assigns false to we add to

We will then show that for any assignment if is true, then likewise, if is false, then

From this fact, we can deduce that entails by repeatedly applying excluded middle to every single atom in and applying our previous lemmas when we are done. Finally, we can transform any sequent into an equivalent sequent in a closed context by repeatedly introducing implications, which lets us apply the argument involving excluded middle.

With that sketch out of the way, let’s dive into the details! First, we define a function tabulate that forms a context from an assignment; this is the from the proof sketch. Some variable munging is required to make Agda happy, but it is more or less what one would expect.

tabulate : (Fin Ξ“ β†’ Bool) β†’ Ctx Ξ“
tabulate {Ξ“ = zero}  ρ = []
tabulate {Ξ“ = suc Ξ“} ρ =
  bump-ctx (tabulate (ρ ∘ fsuc)) β¨Ύ (if ρ 0 then 0 else β€œΒ¬β€ 0)

Next, some small helper lemmas that let us link assignments of atoms and proofs of those atoms. Essentially, these state that if assigns a truth value to then we should be able to prove this under the assumptions

tabulate-atom-true
  : (x : Fin Ξ“)
  β†’ (ρ : Fin Ξ“ β†’ Bool)
  β†’ ρ x ≑ true
  β†’ tabulate ρ ⊒ atom x
tabulate-atom-true {Ξ“ = suc Ξ“} fzero ρ x-true with ρ 0
... | true  = hyp here
... | false = absurd (true≠false $ sym x-true)
tabulate-atom-true {Ξ“ = suc Ξ“} (fsuc x) ρ x-true =
  rename (drop idrn) (bump-proof (tabulate-atom-true x (ρ ∘ fsuc) x-true))

tabulate-atom-false
  : (x : Fin Ξ“)
  β†’ (ρ : Fin Ξ“ β†’ Bool)
  β†’ ρ x ≑ false
  β†’ tabulate ρ ⊒ β€œΒ¬β€ atom x
tabulate-atom-false {Ξ“ = suc Ξ“} fzero ρ x-false with ρ 0
... | false = hyp here
... | true  = absurd (true≠false x-false)
tabulate-atom-false {Ξ“ = suc Ξ“} (fsuc x) ρ x-false =
  rename (drop idrn) (bump-proof (tabulate-atom-false x (ρ ∘ fsuc) x-false))

Now onto the key lemmas. By performing mutual induction on we can note that if is true, then and, conversely, that if is false.

tabulate-true
  : βˆ€ (P : Proposition Ξ“) (ρ : Fin Ξ“ β†’ Bool)
  β†’ ⟦ P ⟧ ρ ≑ true
  β†’ tabulate ρ ⊒ P

tabulate-false
  : βˆ€ (P : Proposition Ξ“) (ρ : Fin Ξ“ β†’ Bool)
  β†’ ⟦ P ⟧ ρ ≑ false
  β†’ tabulate ρ ⊒ β€œΒ¬β€ P

tabulate-true (atom x)  ρ P-true = tabulate-atom-true x ρ P-true
tabulate-true β€œβŠ€β€       ρ P-true = ⊀-intro
tabulate-true β€œβŠ₯”       ρ P-true = absurd (trueβ‰ false $ sym P-true)
tabulate-true (P β€œβˆ§β€ Q) ρ P∧Q-true = ∧-intro
  (tabulate-true P ρ (and-reflect-true-l P∧Q-true))
  (tabulate-true Q ρ (and-reflect-true-r P∧Q-true))
tabulate-true (P β€œβˆ¨β€ Q) ρ P∨Q-true with or-reflect-true P∨Q-true
... | inl P-true = ∨-intro-l (tabulate-true P ρ P-true)
... | inr Q-true = ∨-intro-r (tabulate-true Q ρ Q-true)
tabulate-true (β€œΒ¬β€ P) ρ P-true = tabulate-false P ρ (not-inj P-true)

tabulate-false (atom x)  ρ P-false = tabulate-atom-false x ρ P-false
tabulate-false β€œβŠ€β€       ρ P-false = absurd (trueβ‰ false P-false)
tabulate-false β€œβŠ₯”       ρ P-false = Β¬-intro (hyp here)
tabulate-false (P β€œβˆ§β€ Q) ρ P∧Q-false with and-reflect-false P∧Q-false
... | inl P-false = ¬∧-intro-l (tabulate-false P ρ P-false)
... | inr Q-false = ¬∧-intro-r (tabulate-false Q ρ Q-false)
tabulate-false (P β€œβˆ¨β€ Q) ρ P∨Q-false = ¬∨-intro
  (tabulate-false P ρ (or-reflect-false-l P∨Q-false))
  (tabulate-false Q ρ (or-reflect-false-r P∨Q-false))
tabulate-false (β€œΒ¬β€ P) ρ P-false =
  dneg-intro (tabulate-true P ρ (not-inj P-false))

Next, let be a proposition and let be an assignment for the first atoms in If is a tautology, then This follows from induction on the number of atoms not assigned by If assigns all of the atoms of then by our previous lemma we have For the inductive step, let be a atom not covered by Excluded middle lets us case on the truth value of if it is true, then we extend with and use the induction hypothesis. Likewise, if is false, then we extend with

We’ll then show an appropriate generalisation of the completeness theorem, which will allow us to apply induction. Specifically, we’ll show that, if is a tautology in atoms, and we have an assignment of truth values for the first atoms in then by induction on i.e.Β the number of atoms which does not assign.

tabulate-complete
  : βˆ€ {Ξ” Ξ“} (P : Proposition Ξ“)
  β†’ (Οƒ : Weakening Ξ” Ξ“) (ρ : Fin Ξ” β†’ Bool)
  β†’ [] ⊨ P β†’ shift-ctx Οƒ (tabulate ρ) ⊒ P

The base case is when we have no new atoms: we can then appeal to our previous theorem tabulate-true.

tabulate-complete P done ρ P-taut =
  tabulate-true P ρ (P-taut ρ refl)

For the inductive step, we have some new atoms, and we’re free to assume that any tautology in atoms is a theorem. We use this induction hypothesis twice, once on extended with and once on it extended with We now have proofs that and but by the excluded middle (and disjunction elimination), this suffices to prove

tabulate-complete {Ξ“ = Ξ“} P (weak Οƒ) ρ P-taut = ∨-elim
  (lem (shift-prop Οƒ (atom 0)))
  (subst (_⊒ P)
    (shift-ctx-β¨Ύ Οƒ _ _)
    (tabulate-complete P Οƒ (ρ [ 0 ≔ true ]) P-taut))
  (subst (_⊒ P)
    (shift-ctx-β¨Ύ Οƒ _ _ βˆ™ apβ‚‚ _β¨Ύ_ refl (shift-prop-β€œΒ¬β€ Οƒ (atom 0)))
    (tabulate-complete P Οƒ (ρ [ 0 ≔ false ]) P-taut))

If we start this with an empty assignment, we will construct a gigantic proof, which applies excluded middle to every single atom in and then applies the relevant introduction rules at the leaves. Despite the obvious inefficiency of this procedure, it allows us to deduce that we have completeness for tautologies.

taut-complete : (P : Proposition Ξ“) β†’ [] ⊨ P β†’ [] ⊒ P
taut-complete P taut =
  subst (_⊒ P) (shift-ctx-[] !wk) (tabulate-complete P !wk (λ ()) taut)

Finally, we note that proofs of yield proofs of and likewise for semantic entailments.

⇛-closed : [] ⊒ ψ β€œβ‡›β€ P β†’ ψ ⊒ P
⇛-closed {ψ = []}    p = p
⇛-closed {ψ = ψ β¨Ύ Q} p = β‡’-uncurry (⇛-closed p)

⇛-valid : βˆ€ (ψ : Ctx Ξ“) (P : Proposition Ξ“) β†’ ψ ⊨ P β†’ [] ⊨ (ψ β€œβ‡›β€ P)
⇛-valid []      P valid = valid
⇛-valid (ψ β¨Ύ Q) P valid ρ taut = apβ‚‚ and refl
  (⇛-valid ψ (Q β€œβ‡’β€ P) (Ξ» ρ' ψ-true β†’ it ρ' ψ-true (⟦ Q ⟧ ρ') refl) ρ taut)

This lets us generalize our result for tautologies to any proposition-in-context

complete : ψ ⊨ P β†’ ψ ⊒ P
complete {ψ = ψ} {P = P} valid =
  ⇛-closed $ taut-complete (ψ β€œβ‡›β€ P) (⇛-valid ψ P valid)