Lecture III: Double negation "From any constructive proof we can extract a program." Theorem: If HA ⊢ φ, then ⊩ φ. Proof (part): ─────────── A realizer for (α ⇒ (α ∨ β)) α ⊢ α ∨ β looks like this: # input: a realizer for α # output: a realizer for α ∨ β def e(x): return (0, x) What is the problem with extracting programs from classical proofs? Why don't we have if PA ⊢ φ, then ⊩ φ? For this, the law of excluded middle (LEM) would need to have a realizer. How would such a realizer look like? # input: a proposition A # output: either (0,x) where x is a realizer for A # or (1,y) where y is a realizer for (A ⇒ ⊥) def oracle(A): return (1, lambda x: outer-return (0,x)) # ^^^^^^^^^^^^^^^ a realizer for (A ⇒ ⊥) # input: a realizer for A # output: a realizer for ⊥ There is no realizer for LEM in ordinary programming languages. But there IS a realizer for LEM in languages with support for backtracking. Theorem: If PA ⊢ φ, then ⊩' φ. "From every classical proof, we can extract a program (which might backtrack)." Let's consider the following statement: ∀f : ℕ → ℕ. ∃a : ℕ. ∀b : ℕ. f(a) ≤ f(b). "Every function f : ℕ → ℕ attains a minimum (namely f(a))". This statement doesn't have a constructive proof and there is no realizer in the ordinary sense. But allowing backtracking, a realizer looks like this: # input: a function f : ℕ → ℕ # output: a pair (a,p) where a : ℕ # and p is a function which inputs a number b : ℕ # and outputs a realizer # for f(a) ≤ f(b). def minimum(f): def go(a): return (a, lambda b: if f(a) ≤ f(b) then * else outer-return go(b)) go(0) Observe: While A ∨ ¬A is not constructively provable, its close cousin ¬¬ (A ∨ ¬A) is! Here is a constructive proof: Claim: ((A ∨ ¬A) ⇒ ⊥) ⇒ ⊥. Proof: Assume ((A ∨ ¬A) ⇒ ⊥), we need to verify ⊥. Subclaim: ¬A. Subproof: Assume A. Then in particular A ∨ ¬A. By assumption, ⊥. As ¬A holds, in particular we have A ∨ ¬A. By assumption, ⊥. Idea: Define ∨ᶜˡ, ∃ᶜˡ, =ᶜˡ: α ∨ᶜˡ β := ¬ ¬ (α ∨ β) ∃ᶜˡ x. φ(x) := ¬ ¬ (∃x. φ(x)) x =ᶜˡ y := ¬ ¬ (x = y) Observe: For any A, A ∨ᶜˡ (¬ A). For a formula φ, its double negation translation φ* is obtained by replacing any ∨ by ∨ᶜˡ, any ∃ by ∃ᶜˡ, and any = by =ᶜˡ. For instance (∀f : ℕ → ℕ. ∃a : ℕ. ∀b : ℕ. f(a) ≤ f(b))* is ∀f : ℕ → ℕ. ¬¬∃a : ℕ. ∀b : ℕ. ¬¬ f(a) ≤ f(b). Note that we can "reason under double negation": It is a constructive tautology that (¬¬A ∧ (A ⇒ B)) ⇒ ¬¬B. Even more, we have: (¬¬A ∧ (A ⇒ ¬¬B)) ⇒ ¬¬B. [ this is, in fact, the "bind operation" of a certain monad ] More precisely, we have the following metatheorem: (0) Constructively, ¬¬⊥ ⇒ ⊥. (Never forget: ¬P :≡ (P ⇒ ⊥)) (1) Classically, φ* ⇔ φ. (2) Constructively, ¬¬(φ*) ⇒ φ*. (This makes use of ¬¬¬¬α ⇒ ¬¬α) (3) Constructively, φ* ⇔ ¬¬φ --- IF φ is a "coherent formula" (a formula in which only = ⊤ ⊥ ∧ ∨ ∃, but no ⇒ ∀ occur). (4) Classical logic proves α ⊢ β (from some set Γ of axioms) if and only if constructive logic proves α* ⊢ β* (from the set Γ*, where Γ* = { γ* | γ ∈ Γ }). Note: With the exceptio of (1), Theorems (0) to (4) work just as well for any proposition F in place of ⊥. In this case the negations appearing in the formulation of these theorems (and also in the definition of the double negation translation) mean implication to F instead of implication to the original ⊥. What does a realizer for a statement of the form "∀x:ℕ. P(x)" look like? // input: a number x ∈ ℕ // output: a realizer for P(x) function e(x) { .........compute a realizer r for P(x)....... return r; } What does a realizer for a statement of the form "∀x:ℕ. ¬¬P(x)" look like? // input: a number x ∈ ℕ // output: a realizer for ¬¬P(x) = ((P(x) ⇒ ⊥) ⇒ ⊥) function e(x) { return( // input: a realizer for P(x) ⇒ ⊥ // output: a realizer for ⊥ function (callback) { .........compute a realizer r for P(x)....... // NOTE: this computation↑↑↑ is allowed to make use of the callback! return callback(r); } ); } // That would be nice, but we don't have that: let contents = fetch("https://wikipedia.org/..."); let foo = bar(contents); let abc = baz(foo); // Instead, the API is the following: fetch("https://wikipedia.org/...", function (contents) { bar(contents, function (foo) { baz(foo, function (abc) { ...; }); }); }); ...; ...; ...; Here is a realizer for "∀A. ¬¬(A ∨ ¬A)": # input: a proposition A # output: a realizer for ((A ∨ ¬A) ⇒ ⊥) ⇒ ⊥ def oracle(A): return lambda callback: callback((1, lambda x: callback((0,x)))) # input: a realizer for (A ∨ ¬A) ⇒ ⊥ # output: a realizer for ⊥ Note: The CPS transformation in computer science is the same as the double negation translation in logic. More precisely, the CPS transformation is the shadow cast on programs from the double negation translation. Theorem: (5) If classical logic proves α ⊢ β (from some set Γ of axioms), then also constructive logic proves α ⊢ β (from the same set Γ of axioms) ---assuming that α ⊢ β and all the axioms in Γ are coherent sequents (that is, their antecedents and consequents are all coherent formulas; free variables are allowed). Proof: Choosing F :≡ β, we have, constructively, (3) (3) α ⊢ ¬¬α ⊢ α* ⊢ β* ⊢ ¬¬β ≡ ((β ⇒ F) ⇒ F) ⊢ β. ((β ⇒ β) ⇒ β) ⊢ β. This theorem has various names: Friedman's trick, Barr's theorem, escaping the continuation monad, ... Moral: If at the end of the day all we want is to prove a coherent sequent (from a set of coherent sequents as axioms), then we can regard the law of excluded middle as a useful fiction. Not actually available in constructive mathematics, but we can often rightfully pretend that it is. This holds even if auxiliary results required for the proof fail the coherence requirement and cannot individually be constructivized using Theorem (5)! Example: The statement that every function ℕ → ℕ attains a minimum cannot individually be constructivized. But we can nevertheless use it as a tool in a constructive proof of Dickson's Lemma!