The classical boundary

Set theory as most readers know it is classical: excluded middle is ambient air. The host, however, is constructive, and this book keeps the boundary between the two visible as a matter of law. The rule, fixed in the Charter, is that classical principles enter as explicit parameters, never as global assumptions: a chapter that reasons classically says so in its own interface, the type checker polices the boundary, and there is not a single postulate in this book. This chapter states the one classical principle everything later appeals to, and banks its two basic dividends: the impredicativity interfaces of the previous chapter, redeemed.

{-# OPTIONS --cubical --safe --guardedness #-}

module Base.Classical where

open import Base.Prelude
open import Base.Truth
open import Base.Impredicativity
  using ( isSmall; Resizing; HPropSmallness; Impredicativity )
open import Cubical.Data.Sum using ( _⊎_; inl; inr )
import Cubical.Data.Empty as Empty
open import Cubical.Data.Bool using ( Bool; true; false )
open import Cubical.Data.Unit using ( tt* )
open import Cubical.Foundations.Equiv using ( propBiimpl→Equiv )
open import Cubical.Foundations.Isomorphism using ( iso; isoToEquiv )
open import Cubical.Functions.Logic using ( ⇔toPath )

The statement

LEM :    Type (ℓ-suc )
LEM  = (P : hProp )   P   ( P   Empty.⊥)

LEM says: every proposition at level is either true or false. Why this particular form? In univalent foundations a type-level global choice or excluded middle is inconsistent with univalence; what can consistently be assumed is exactly this propositional form, quantified over hProp. The foundation itself forces the honest phrasing.

A chapter that works classically takes (lem : ∀ {ℓ} → LEM ℓ) in its module telescope and passes it along when importing other classical chapters. The consequence is worth pausing on: whether a theorem uses excluded middle is a compile-time fact. The classical debt is part of a chapter's type, visible at every import site, instead of an invisible global axiom; and since nothing is postulated, the whole book carries Agda's --safe seal.

One transfer lemma before the dividends. Excluded middle passes downward through the levels: to decide a small proposition, lift its underlying type one universe up, decide there, and lower the verdict. So a single instance of

LEM at a higher level silently covers every level below it, a fact the end of this chapter spends.

lowerLEM :  {}  LEM (ℓ-suc )  LEM 
lowerLEM {} lem P = fromLifted (lem lifted)
  where
  lifted : hProp (ℓ-suc )
  lifted = Lift  P  , λ x y  cong lift (P .snd (lower x) (lower y))
  fromLifted :  lifted   ( lifted   Empty.⊥)   P   ( P   Empty.⊥)
  fromLifted (inl p)  = inl (lower p)
  fromLifted (inr np) = inr  p  np (lift p))

The first dividend: a small classifier

Classically a proposition has only two possible values, and that innocent remark has universe-level teeth. First the classifier: HPropSmallness, named in the previous chapter, asks for a small type equivalent to hProp. Classically it is Lift Bool, at every level . The construction is arranged so that all the real work is constructive: the four helpers below take a decision of a proposition (a proof, or a refutation) as an ordinary argument, and excluded middle enters only at the final assembly, to supply those decisions.

First the chapter cashes the promise of the scope discipline: it opens the canonical instance, taking exactly its and . From here on the two symbols mean the hProp algebra's truth values, and by definitional transparency this is the pair (⊥* , isProp⊥*) itself. Then the decoding direction, from Booleans to propositions: decodeB sends true to

and false to . The domain is

Lift {ℓ-zero} {ℓ} Bool rather than bare Bool because Bool

lives at the bottom level while the propositions live at : the lifted copy is what lets the two ends of the coming equivalence share a universe.

open module Canonical { : Level} = TruthAlgebra (hPropAlgebra ) using ( ;  )

private
  decodeB :  {}  Lift {ℓ-zero} {} Bool  hProp 
  decodeB (lift true)  = 
  decodeB (lift false) = 

The encoding direction hides an asymmetry. Its would-be signature is

hProp ℓ → Lift Bool, the exact inverse of decodeB, but no such function can be defined: unlike lift true and lift false, an arbitrary proposition P is not a pattern one can match on, so there is no case split "if P holds, otherwise" to write. encodeB therefore takes one extra argument, a decision d of P, and matches on that: a proof gives

true, a refutation gives false. The shape mirrors

decodeB, but the thing being inspected is the handed-over decision, never the proposition itself. No excluded middle here; the decision is an input.

  encodeB :  {} (P : hProp )   P   ( P   Empty.⊥)  Lift {ℓ-zero} {} Bool
  encodeB P (inl _) = lift true
  encodeB P (inr _) = lift false

One round trip: decoding the encoding of P gives back P itself. The tool is

⇔toPath, the library's propositional extensionality: between propositions, maps in both directions already make a path (in this book that principle is a theorem, not an axiom). If the decision is a proof p, the goal is

P, and both directions are trivial: from to P the answer p is already in hand, and back the other way everything maps to

tt*, the inhabitant of . If the decision is a refutation np, the goal is P: out of ⊥* nothing needs saying, which is what the absurd pattern λ () says, and any alleged proof p of P is crushed by np, with Empty.rec eliminating the resulting absurdity.

  secB :  {} (P : hProp ) (d :  P   ( P   Empty.⊥))
        decodeB (encodeB P d)  P
  secB P (inl p)  = ⇔toPath  _  p)  _  tt*)
  secB P (inr np) = ⇔toPath  ())  p  Empty.rec (np p))

The other round trip: encoding the decoding of a Boolean b gives back b. One subtlety deserves attention: at assembly time it is excluded middle that will decide

decodeB b, and nothing promises which decision it hands over. So retrB

proves the equation for every decision d, by four cases. true with a proof: refl. true with an alleged refutation n⊤: impossible, since does hold, and n⊤ tt* is the absurdity. false with an alleged proof: that proof is a term of ⊥*, and the absurd pattern () closes the case before any equation is owed. false with a refutation: refl.

  retrB :  {} (b : Lift {ℓ-zero} {} Bool)
          (d :  decodeB b   ( decodeB b   Empty.⊥))
         encodeB (decodeB b) d  b
  retrB (lift true)  (inl _)  = refl
  retrB (lift true)  (inr n⊤) = Empty.rec (n⊤ tt*)
  retrB (lift false) (inl ())
  retrB (lift false) (inr _)  = refl

The assembly. iso packages the four pieces (decode; decide, then encode; the two round trips), and isoToEquiv upgrades the isomorphism to an equivalence. Count the occurrences of lem: three, and all three do the same job, supplying the decisions the constructive helpers asked for as inputs. That is the entire footprint of excluded middle in this dividend.

lem→hPropSmallness :  {}  LEM   HPropSmallness 
lem→hPropSmallness lem = Lift Bool , isoToEquiv (iso decodeB
   P  encodeB P (lem P))
   P  secB P (lem P))
   b  retrB b (lem (decodeB b))))

The second dividend: propositional resizing

Second, Resizing: every proposition one universe up is small. Classically, decide the proposition: if it holds it is equivalent to , if it fails to , and both are small.

As before, the work is done from a handed-over decision, and P .snd (the propositionality proof, as the Prelude promised) is used directly. If P holds, the small stand-in is the of level : between two propositions, maps in both directions already form an equivalence of underlying types, which is what propBiimpl→Equiv builds from the two propositionality proofs and the two maps; from P to everything goes to tt*, and back the other way p is in hand. If P fails, the stand-in is , with the same two absurdity moves as in secB. Note the shift against the first dividend: there the output was a path between propositions (⇔toPath), here it is an equivalence between their underlying types, so the same pair of maps is fed to

propBiimpl→Equiv instead.

private
  resizeDec :  {} (P : hProp (ℓ-suc ))   P   ( P   Empty.⊥)
             isSmall P
  resizeDec P (inl p)  =  , propBiimpl→Equiv (P .snd) ( .snd)  _  tt*)  _  p)
  resizeDec P (inr np) =  , propBiimpl→Equiv (P .snd) ( .snd)
                                p  Empty.rec (np p))  ())

The assembly is one line: decide P with excluded middle, hand the decision over. The signature is the strength bookkeeping: this dividend consumes excluded middle at the higher level ℓ-suc ℓ, once, and nothing more.

lem→resizing :  {}  LEM (ℓ-suc )  Resizing 
lem→resizing lem P = resizeDec P (lem P)

Redeeming the packing

The previous chapter packed the two instruments as Impredicativity, by co-consumption, not implication: neither derives the other. Only the excluded middle redeems both at once, and from a single instance at the higher level: resizing consumes it as is, and lowerLEM hands the classifier its lower copy. Part 3 will name its exact prices in this packing.

lem→impredicativity :  {}  LEM (ℓ-suc )  Impredicativity 
lem→impredicativity lem = record
  { resizing       = lem→resizing lem
  ; hPropSmallness = lem→hPropSmallness (lowerLEM lem) }

Recap

Excluded middle is stated as the interface LEM, taken by chapters as a parameter and never assumed globally; the boundary between constructive and classical mathematics is therefore a compile-time fact. The previous chapter's two interfaces are banked as dividends, the small classifier by

lem→hPropSmallness and propositional resizing by

lem→resizing, and the packing Impredicativity is redeemed whole (lem→impredicativity). Part 3 will spend exactly this packing: it prices, for the cumulative hierarchy V, the smallness assumptions behind full separation and power set.