Search over uniformly continuous decidable predicates on infinite collections of types. (Part I)

Todd Waugh Ambridge, 15th December 2021

Table of Contents

  1. Overview
  2. Searchable types
  3. Closeness functions and extended naturals
  4. Discrete closeness function
  5. Discrete-sequence closeness function
  6. Continuity and continuously searchable types
  7. Main result

Overview

In this blog post I lay the groundwork necessary to safely formalise the Tychonoff theorem for searchable types.

Beginning with a small constructive type theory, we re-introduce the notion of ‘searchable types’ [1]. We then introduce the notion of closeness function, our version of a metric in this setting, to allow us to define ‘continuously searchable’ types. The main result for this first blog post is that discrete-sequence types (types ℕ → X where X has decidable equality) are continuously searchable. A corollary to this is that the Cantor space is continuously serchable.

In Part II, I use the framework built here to prove the Tychonoff theorem safely. This has been previously formalised by Martín Escardó with Agda’s termination checker turned off.

[1] Escardo, Martin. (2007). Infinite sets that admit fast exhaustive search. Proceedings - Symposium on Logic in Computer Science. 443 - 452. 10.1109/LICS.2007.25.

{-# OPTIONS --without-K --exact-split --safe #-}

open import SpartanMLTT hiding (decidable)
open import Two-Properties hiding (zero-is-not-one)
open import NaturalsOrder

module InfiniteSearch1 (fe : {𝓤 𝓥 : Universe}  {X : 𝓤 ̇ } {Y : X  𝓥 ̇ } {f g : Π Y}
                            f  g  f  g) where

Searchable types

In [1], a type X is called searchable if, given any predicate p : X → {tt,ff}, we can find some x : X such that if there is some x₀ such that p(x₀) ≡ tt then also p(x) ≡ tt.

This definition can be written in constructive type theory by using a boolean type or, as we do here, using decidable predicates.

A type X is decidable if we can decide whether we have a construction of X or ¬ X.

A type family p : X → 𝓤₀ on a type X is decidable if it is everywhere decidable. In univalent foundations, we often call a truncated type family a predicate. For the purposes of this work, we do not use truncation, and refer to any type family as a predicate as they play the role of Boolean-valued predicates in [1].

predicate : (X : 𝓤 ̇ )  (𝓤₀ )  𝓤 ̇
predicate X = X  𝓤₀ ̇ 

decidable : 𝓤 ̇  𝓤 ̇
decidable X = X + ¬ X

everywhere-decidable : {X : 𝓤 ̇}  predicate X  𝓤 ̇
everywhere-decidable {𝓤} {X} p = Π x  X , decidable (p x)

d-predicate : 𝓤 ̇  (𝓤₀ )  𝓤 ̇
d-predicate X = Σ p  (X  𝓤₀ ̇ ) , everywhere-decidable p

A type is therefore searchable if, given any decidable predicate, we can construct x : X such that, if there is some x₀ : X such that p(x₀), then p(x).

searchable : 𝓤 ̇  (𝓤₀ )  𝓤 ̇
searchable X = Π (p , _)  d-predicate X , Σ x  X , (Σ x₀  X , p x₀  p x)

The notion of searchability coincides with that of compactness. This can be seen fully in the file CompactTypes by Escardó, where the above construction is equivalent to that named compact∙ in that file.

The exception to this is that searchability implies inhabitance, whereas the empty type 𝟘 is compact.

searchable-types-are-inhabited : {X : 𝓤 ̇ }  searchable X  X
searchable-types-are-inhabited {𝓤} {X} S = pr₁ (S trivial-predicate)
 where
   trivial-predicate : d-predicate X
   trivial-predicate =  x  𝟙) ,  x  inl )

Any finite type is trivially searchable, as are finite products and co-products of searchable types.

The type of Boolean values 𝟚 ≔ {₀,₁} is searchable by the following argument: - Using decidability, we ask if satisfies the predicate p being searched, i.e. we ask if p(₁) is inhabited. - If p(₁), then we return — thus, trivially, if there is some x₀ : 𝟚 such that p(x₀) then also p(₁). - If p(₁) is uninhabited (i.e. we have a function of type ¬ p(₁) ≔ p(₁) → 𝟘) then we return — given some x₀ : 𝟚 such that p(x₀) we prove that p(₀) by case analysis of x₀. - If x₀ = ₀ then p(₀). - If x₀ = ₁ then p(₁). This case is absurd, however, as we already showed that p ₁ is uninhabited. We discard this case using the elimination rule of the empty type 𝟘.

𝟚-is-searchable : searchable 𝟚
𝟚-is-searchable (p , d) = γ (d ) where
  γ : decidable (p )  Σ x₀  𝟚 , (Σ p  p x₀)
  γ (inl p₁) =  ,  _  p₁)
  γ (inr f ) =  , δ where
    δ : Σ p  p 
    δ ( , p₀) = p₀
    δ ( , p₁) = 𝟘-elim (f p₁)

Searchability of the natural numbers, however, is a constructive taboo and is equivalent to the limited principle of omniscience (LPO).

LPO states that, given any infinite sequence of binary numbers, either all are or we have some n : ℕ such that (f n) ≡ ₁.

We define LPO' below, which implies LPO.

LPO  : 𝓤₀ ̇
LPO  = Π f  (  𝟚)             , (Σ n   , f n  ) + (Π n   , f n  )

LPO' : 𝓤₁ ̇
LPO' = Π (p , d)  d-predicate  , (Σ n   , p n)     + (Π n   , ¬ (p n))

ℕ-searchable-implies-LPO : searchable   LPO'
ℕ-searchable-implies-LPO S (p , d) = Cases (d x₀) (inl  left) (inr  right)
 where
  x₀ : 
  x₀ = pr₁ (S (p , d))
  γ₀ : Σ p  p x₀
  γ₀ = pr₂ (S (p , d))
  left  :    p x₀   Σ x   ,   p x
  left   px₀      = x₀ , px₀
  right : ¬ (p x₀)  Π x   , ¬ p x
  right ¬px₀ x px = ¬px₀ (γ₀ (x , px))
  
LPO-implies-ℕ-searchable : LPO'  searchable 
LPO-implies-ℕ-searchable L (p , d) = Cases (L (p , d)) left right
 where
  left  : Σ x   ,   p x  Σ x₀   , (Σ p  p x₀)
  left  (x₀ , px₀) = x₀ , λ _  px₀
  right : Π x   , ¬ p x  Σ x₀   , (Σ p  p x₀)
  right f = 0 ,  (x , px)  𝟘-elim (f x px))

Perhaps surprisingly however, there are some infinite types that are searchable. The “seemingly impossible functional program”, written in Haskell, searches predicates on the Cantor type ℕ → 𝟚.

The magic here is in fact performed by continuity! In various systems for constructive mathematics, every predicate p over ℕ → 𝟚 is uniformly continuous, and therefore only a finite amount of information is required to construct every finite prefix of α₀ : ℕ → 𝟚 satisfying Σ p → p α₀.

However, although the Haskell program presumably terminates given any predicate, formalising this program in Agda is more subtle. By implicitly assuming the predicate to be searched is uniformly continuous, Agda’s termination checker fails on the proof that ℕ → 𝟚 is uniformly continuous. This can be seen in the file CountableTychonoff, which has the termination checker turned off, and hence is an ‘unsafe’ module.

We instead require a specific definition of a ‘uniformly continuous predicate’ over ℕ → 𝟚. This is relatively straightforward:

_≡⟦_⟧_ : {X : 𝓤 ̇ }  (  X)    (  X)  𝓤 ̇
α ≡⟦ m  β = Π k   , (k ≤ℕ m  α k  β k)

is-u-continuous-𝟚ᴺ : ((  𝟚)  𝓤₀ ̇ )  𝓤₀ ̇
is-u-continuous-𝟚ᴺ p = Σ m   , ((α β :   𝟚)  α ≡⟦ m  β  p α  p β)

Martín Escardó’s file CantorSearch uses this explicit definition of uniform continuity to prove that ℕ → 𝟚 is searchable on such explicitly-defined uniformly continuous predicates.

Using the definition of uniform continuity as above, this can be easily extended to any type of infinite sequences ℕ → X where X is a discrete type.

However, as searchable types coincide with the concept of compactness, we want a full-blown constructive formalisation of the Tychonoff theorem:

Theorem (Tychonoff). Given T : ℕ → 𝓤 is a family of types indexed by the natural numbers, such that every (T n) : 𝓤 is searchable, the type (Π T) : 𝓤 is searchable.

This theorem of course implies that types ℕ → X (where X is discrete) are searchable; but in order to prove the Tychonoff theorem we need a much more general definition of uniform continuity that does not require the types (T n) to be disrete.

Closeness functions and extended naturals

We now introduce the idea of a closeness function on a given type X. These are binary functions c : X × X → ℕ∞.

ℕ∞ is the type of extended natural numbers (i.e.  extended with a point at infinity), encoded as decreasing infinitary binary sequences.

_≥₂_ : 𝟚  𝟚  𝓤₀ ̇
a ≥₂ b = b    a  

decreasing-binary-seq : (  𝟚)  𝓤₀ ̇
decreasing-binary-seq α = Π n   , α n ≥₂ α (succ n)

ℕ∞ : 𝓤₀ ̇ 
ℕ∞ = Σ decreasing-binary-seq

Any natural number n : ℕ can be mapped to an extended natural k ↑ : ℕ∞, which is the sequence with k-many s followed by infinitely-many s.

e.g. 5 ↑ ≡ ₁₁₁₁₁₀₀₀₀₀₀₀ ⋯

∞ : ℕ∞ is represented as the sequence with infinitely-many 1s.

i.e. ∞ ≡ ₁₁₁₁₁₁₁₁₁₁₁₁ ⋯

_::_ : {X : 𝓤 ̇ }  X  (  X)  (  X)
(x :: α) 0        = x
(x :: α) (succ n) = α n

repeat : {X : 𝓤 ̇ }  X  (  X)
repeat x = λ n  x

_↑ :   ℕ∞
0       = repeat        ,  n ₀≡₁  ₀≡₁)
succ n  =  :: pr₁ (n ) , γ
 where
   γ : decreasing-binary-seq ( :: pr₁ (n ))
   γ 0 _ = refl
   γ (succ k) = pr₂ (n ) k
   
 : ℕ∞
 = repeat  ,  n ₁≡₁  ₁≡₁)

Given two extended naturals α , β : ℕ∞, α ≼ β if everywhere α has s β also has s.

Given any α : ℕ∞, clearly (0 ↑) ≼ α and α ≼ ∞.

_≼_ : ℕ∞  ℕ∞  𝓤₀ ̇
(α , _)  (β , _) = Π n   , (α n    β n  )

0-minimal : (α : ℕ∞)  (0 )  α
0-minimal α k ()

∞-maximal : (α : ℕ∞)  α    
∞-maximal α k αₖ≡₁ = refl

The minimum of two extended naturals is defined below.

min : ℕ∞  ℕ∞  ℕ∞
min α β =  n  min𝟚 (pr₁ α n) (pr₁ β n)) , γ
 where
   γ : decreasing-binary-seq  n  min𝟚 (pr₁ α n) (pr₁ β n))
   γ n q = Lemma[a≡₁→b≡₁→min𝟚ab≡₁] (pr₂ α n r) (pr₂ β n s)
    where
      r : pr₁ α (succ n)  
      r = Lemma[min𝟚ab≡₁→a≡₁] q
      s : pr₁ β (succ n)  
      s = Lemma[min𝟚ab≡₁→b≡₁] q

Now, a binary function c : X × X → ℕ∞ is a closeness function (referred to for brevity in the Agda code as a ‘clofun’) if it satisfies the following four properties:

  1. A construction is infinitely close to itself ∀ x → c (x , x) ≡ ∞

  2. Constructions that are infinite close are equal ∀ x y → c (x , y) ≡ ∞ → x ≡ y

  3. Symmetricity ∀ x y → c (x , y) ≡ c (y , x)

  4. Triangle ultrametric property ∀ x y z → min (c (x , y)) (c (y , z)) ≼ c (x , z)

From these properties, we can see clearly the relationship with a metric. In fact, an ultrametric (a metric with a strengthened triangle equality property) can be defined using a closeness function easily:

m : X × X → ℝ
m (x , y) ≡ 1 / (c(x , y) + 1)

Where, by convention, 1 / ∞ ≡ 0.

record is-clofun {X : 𝓤 ̇ } (c : X × X  ℕ∞) : 𝓤 ̇ where
  field
    equal→inf-close : (x     : X)  c (x , x)  
    inf-close→equal : (x y   : X)  c (x , y)    x  y
    symmetricity : (x y   : X)  c (x , y)  c (y , x)
    ultrametric : (x y z : X)  min (c (x , y)) (c (y , z))  c (x , z)

Discrete closeness function

We briely introduce a closeness function for discrete types, and a closeness function for discrete-sequence types.

A type is discrete if it has decidable equality.

is-discrete : 𝓤 ̇  𝓤 ̇
is-discrete X = (x y : X)  decidable (x  y)

The closeness function for a discrete type is defined easily by cases:

c (x , y) ≡   ∞    if x ≡ y
              0 ↑  otherwise
discrete-c' : {X : 𝓤 ̇ }  ((x , y) : X × X)  decidable (x  y)  ℕ∞
discrete-c' (x , y) (inl x≡y) = 
discrete-c' (x , y) (inr x≢y) = 0 

discrete-clofun : {X : 𝓤 ̇ }  is-discrete X  (X × X  ℕ∞)
discrete-clofun d (x , y) = discrete-c' (x , y) (d x y)

Note that we use the helper function discrete-c'. This is to allow the Agda synthesizer to recognise when a given construction of the type decidable (x ≡ y) (for some x,y : X) is constructed as inl x≡y (where x≡y : x ≡ y) or inr x≢y (where x≢y : ¬ (x ≡ y)).

Using the synthesizer in this way allows us to easily prove the four closeness function properties for the helper function, just using pattern matching on the given construction of decidable (x ≡ y).

discrete-c'-eic : {X : 𝓤 ̇ }  (x : X)
                 (dxx : decidable (x  x))
                 discrete-c' (x , x) dxx  
discrete-c'-eic x (inl x≡x) = refl
discrete-c'-eic x (inr x≢x) = 𝟘-elim (x≢x refl)

zero-is-not-one :   
zero-is-not-one ()

discrete-c'-ice : {X : 𝓤 ̇ }  (x y : X)
                       (dxy : decidable (x  y))
                       discrete-c' (x , y) dxy    x  y
discrete-c'-ice x y (inl x≡y) cxy≡∞ = x≡y
discrete-c'-ice x y (inr x≢y) cxy≡∞ = 𝟘-elim (Zero-not-∞ cxy≡∞)
 where
   Zero-not-∞ : (0 )  
   Zero-not-∞ 0≡∞ = 𝟘-elim (zero-is-not-one (ap  -  pr₁ - 0) 0≡∞))
                                 
discrete-c'-sym : {X : 𝓤 ̇ }  (x y : X)
                 (dxy : decidable (x  y))
                 (dyx : decidable (y  x))
                 discrete-c' (x , y) dxy  discrete-c' (y , x) dyx
discrete-c'-sym x y (inl x≡y) (inl y≡x) = refl
discrete-c'-sym x y (inr x≢y) (inr y≢x) = refl
discrete-c'-sym x y (inl x≡y) (inr y≢x) = 𝟘-elim (y≢x (x≡y ⁻¹))
discrete-c'-sym x y (inr x≢y) (inl y≡x) = 𝟘-elim (x≢y (y≡x ⁻¹))
                                           
discrete-c'-ult : {X : 𝓤 ̇ }  (x y z : X)
                 (dxy : decidable (x  y))
                 (dyz : decidable (y  z))
                 (dxz : decidable (x  z))
                 min (discrete-c' (x , y) dxy) (discrete-c' (y , z) dyz)
                      discrete-c' (x , z) dxz
discrete-c'-ult x  y  z       _          _    (inl x≡z ) _ _ = refl
discrete-c'-ult x  y  z (inl x≡y ) (inr y≢z ) (inr x≢z ) _   = id
discrete-c'-ult x  y  z (inr x≢y )       _    (inr x≢z ) _   = id
discrete-c'-ult x .x .x (inl refl) (inl refl) (inr x≢x )     = 𝟘-elim (x≢x refl)

We can now easily prove that any discrete type has a closeness function that satisfies the necessary properties.

discrete-is-clofun : {X : 𝓤 ̇ }  (ds : is-discrete X)
                        is-clofun (discrete-clofun ds)
is-clofun.equal→inf-close (discrete-is-clofun ds) x
 = discrete-c'-eic x      (ds x x)
is-clofun.inf-close→equal (discrete-is-clofun ds) x y
 = discrete-c'-ice x y    (ds x y)
is-clofun.symmetricity    (discrete-is-clofun ds) x y
 = discrete-c'-sym x y    (ds x y) (ds y x)
is-clofun.ultrametric     (discrete-is-clofun ds) x y z
 = discrete-c'-ult x y z  (ds x y) (ds y z) (ds x z)

Discrete-sequence closeness function

The closeness function for a type (ℕ → X) where X is discrete is defined pointwise by cases as follows:

c (α , β) n ≡ ₁,    if x ≡⟦ n ⟧ y,
              ₀,    otherwise.

We again want to use a helper function to allow us to prove properties using the Agda synthesizer just by using pattern matching on the type decidable (α ̄≡⟦ n ⟧ β).

To do this we first prove the following lemma.

discrete-decidable-seq : {X : 𝓤 ̇ }  is-discrete X
                        (α β :   X)  (n : )  decidable (α ≡⟦ n  β)
discrete-decidable-seq d α β 0 = Cases (d (α 0) (β 0)) (inl  γₗ) (inr  γᵣ)
 where
   γₗ :    α 0  β 0      α ≡⟦ 0  β
   γₗ e 0 _ = e
   γᵣ : ¬ (α 0  β 0)  ¬ (α ≡⟦ 0  β)
   γᵣ f α≡⟦0⟧β = 𝟘-elim (f (α≡⟦0⟧β 0 ))
discrete-decidable-seq d α β (succ n)
 = Cases (discrete-decidable-seq d α β n) γ₁ (inr  γ₂)
 where
   γ₁ : α ≡⟦ n  β  decidable (α ≡⟦ succ n  β)
   γ₁ α≈β = Cases (d (α (succ n)) (β (succ n))) (inl  γₗ) (inr  γᵣ)
    where
      γₗ :     α (succ n)  β (succ n)     α ≡⟦ succ n  β
      γₗ e k k≤n = Cases (≤-split k n k≤n)
                      k≤n   α≈β k k≤n)
                      k≡sn  transport  -  α -  β -) (k≡sn ⁻¹) e)
      γᵣ : ¬ (α (succ n)  β (succ n))  ¬ (α ≡⟦ succ n  β)
      γᵣ g α≡⟦sn⟧β = g (α≡⟦sn⟧β (succ n) (≤-refl n))
   γ₂ : ¬ (α ≡⟦ n  β)  ¬ (α ≡⟦ succ n  β)
   γ₂ f = f   α≈β k k≤n  α≈β k (≤-trans k n (succ n) k≤n (≤-succ n)))

We now define the closeness function using a helper function.

discrete-seq-c' : {X : 𝓤 ̇ }  ((α , β) : (  X) × (  X))
                  (n : )  decidable (α ≡⟦ n  β)  𝟚
discrete-seq-c' (α , β) n (inl α≡⟦n⟧β) = 
discrete-seq-c' (α , β) n (inr α≡⟦n⟧β) = 

discrete-seq-c'-dec : {X : 𝓤 ̇ }  ((α , β) : (  X) × (  X))
                     (n : )  (d₁ : decidable (α ≡⟦      n  β))
                                (d₂ : decidable (α ≡⟦ succ n  β))
                     (discrete-seq-c' (α , β) n d₁ ≥₂ discrete-seq-c' (α , β) (succ n) d₂)
discrete-seq-c'-dec (α , β) n (inl  α≡⟦n⟧β) (inl  α≡⟦sn⟧β) _ = refl
discrete-seq-c'-dec (α , β) n (inl  α≡⟦n⟧β) (inr ¬α≡⟦sn⟧β) _ = refl
discrete-seq-c'-dec (α , β) n (inr ¬α≡⟦n⟧β) (inl  α≡⟦sn⟧β) refl
 = 𝟘-elim (¬α≡⟦n⟧β  k k<n  α≡⟦sn⟧β k (≤-trans k n (succ n) k<n (≤-succ n))))
discrete-seq-c'-dec (α , β) n (inr ¬α≡⟦n⟧β) (inr ¬α≡⟦sn⟧β) = 𝟘-elim  zero-is-not-one

discrete-seq-clofun : {X : 𝓤 ̇ }  is-discrete X  ((  X) × (  X)  ℕ∞)
discrete-seq-clofun ds (α , β)
 =  n  discrete-seq-c'     (α , β) n (discrete-decidable-seq ds α β       n))
 ,  n  discrete-seq-c'-dec (α , β) n (discrete-decidable-seq ds α β       n)
                                        (discrete-decidable-seq ds α β (succ n)))

In order to show that the discrete-sequence closeness function satisfies the four necessary properties, we first need a way to show that two extended naturals are equal.

Of course, by function extensionality, two sequences α,β : ℕ → X are equal α ≡ β if they are equivalent α ∼ β ≔ Π i ꞉ ℕ , (α i ≡ β i).

seq-equals : {X : 𝓤 ̇ } {α β :   X}  α  β  α  β
seq-equals α∼β = fe α∼β

However, recall that an extended natural consists of both a binary sequence and a proof that the sequence is descending.

Therefore, in order to show that, for (α , α-dec),(β , β-dec) : ℕ∞, (α , α-dec) ≡ (β , β-dec) we need to construct objects of types:

  1. α ≡ β, for α,β : ℕ → 𝟚,

  2. α-dec ≡ β-dec, for α-dec : decreasing-binary-seq α and, by 1., β-dec : decreasing-binary-seq α.

Constructing an element of 2. is non-trivial; but, it is a subsingleton.

In homotopy type theory, a type X is called a ‘prop’ or a ‘subsingleton’ if, for any x,y : X, x ≡ x. This means that the type has at most one element.

is-subsingleton : 𝓤 ̇  𝓤 ̇
is-subsingleton X = (x y : X)  x  y

Given a type family Y : X → 𝓤 ̇ if, for all x : X, Y x is a subsingleton, then Π Y is also a subsingleton.

Π-is-subsingleton : {X : 𝓤 ̇ } {Y : X  𝓥 ̇ }
                   ((x : X)  is-subsingleton (Y x))
                              is-subsingleton (Π Y)
Π-is-subsingleton Y-is-prop f g = fe  x  Y-is-prop x (f x) (g x))

A type X is called a ‘set’ if, for any x,y : X, the type x ≡ y is a subsingleton.

is-set : 𝓤 ̇  𝓤 ̇
is-set X = (x y : X)  is-subsingleton (x  y)

𝟚 is a set, and thus the relation _≥₂_ is prop-valued. This allows us to prove that the type decreasing-binary-seq α, for any α : ℕ → 𝟚, is a prop — thus allowing us to construct 2..

𝟚-is-set : is-set 𝟚
𝟚-is-set   refl refl = refl
𝟚-is-set   refl refl = refl

≥₂-is-prop : (a b : 𝟚)  is-subsingleton (a ≥₂ b)
≥₂-is-prop a b = Π-is-subsingleton  _  𝟚-is-set a )

decreasing-prop : (α :   𝟚)  is-subsingleton (decreasing-binary-seq α)
decreasing-prop α = Π-is-subsingleton  n  ≥₂-is-prop (α n) (α (succ n)))

sigma-prop-equals : {X : 𝓤 ̇ } {Y : X  𝓥 ̇ }
                   {(x₁ , y₁) (x₂ , y₂) : Σ x  X , Y x}
                   x₁  x₂
                   ((x : X)  is-subsingleton (Y x))
                   (x₁ , y₁)  (x₂ , y₂)
sigma-prop-equals {𝓤} {𝓥} {X} {Y} {(x₁ , Yx₁)} {(.x₁ , Yx₂)} refl Y-is-prop
 = ap (x₁ ,_) (Y-is-prop x₁ Yx₁ Yx₂)

ℕ∞-equals : {(α , α-dec) (β , β-dec) : ℕ∞}  α  β  (α , α-dec)  (β , β-dec)
ℕ∞-equals α∼β = sigma-prop-equals (fe α∼β) decreasing-prop

We now prove the four necessary properties using the helper function…

discrete-seq-c'-eic : {X : 𝓤 ̇ }  (α :   X)
                      (n : )  (d : decidable (α ≡⟦ n  α))
                      discrete-seq-c' (α , α) n d  
discrete-seq-c'-eic α n (inl  α≡⟦n⟧α) = refl
discrete-seq-c'-eic α n (inr ¬α≡⟦n⟧α) = 𝟘-elim (¬α≡⟦n⟧α  k k≤n  refl))

discrete-seq-c'-ice : {X : 𝓤 ̇ }  (α β :   X)
                      (n : )  (d : decidable (α ≡⟦ n  β))
                      discrete-seq-c' (α , β) n d  
                      α n  β n
discrete-seq-c'-ice α β n (inl  α≡⟦n⟧β) cαβn≡₁ = α≡⟦n⟧β n (≤-refl n)
discrete-seq-c'-ice α β n (inr ¬α≡⟦n⟧β) ()

discrete-seq-c'-sym : {X : 𝓤 ̇ } (α β :   X)
                      (n : )  (d₁ : decidable (α ≡⟦ n  β))
                                 (d₂ : decidable (β ≡⟦ n  α))
                      discrete-seq-c' (α , β) n d₁  discrete-seq-c' (β , α) n d₂
discrete-seq-c'-sym x y n (inl  α≡⟦n⟧β) (inl  β≡⟦n⟧α) = refl
discrete-seq-c'-sym x y n (inr ¬α≡⟦n⟧β) (inr ¬β≡⟦n⟧α) = refl
discrete-seq-c'-sym x y n (inl  α≡⟦n⟧β) (inr ¬β≡⟦n⟧α)
 = 𝟘-elim (¬β≡⟦n⟧α  k k<n  α≡⟦n⟧β k k<n ⁻¹))
discrete-seq-c'-sym x y n (inr ¬α≡⟦n⟧β) (inl  β≡⟦n⟧α)
 = 𝟘-elim (¬α≡⟦n⟧β  k k<n  β≡⟦n⟧α k k<n ⁻¹))

discrete-seq-c'-ult : {X : 𝓤 ̇ } (α β η :   X)
                      (n : )  (d₁ : decidable (α ≡⟦ n  β))
                                (d₂ : decidable (β ≡⟦ n  η))
                                (d₃ : decidable (α ≡⟦ n  η))
                      min𝟚 (discrete-seq-c' (α , β) n d₁)
                            (discrete-seq-c' (β , η) n d₂)  
                      discrete-seq-c' (α , η) n d₃  
discrete-seq-c'-ult α β η n _             _             (inl  α≡⟦n⟧η) _ = refl
discrete-seq-c'-ult α β η n (inl α≡⟦n⟧β)  (inl  β≡⟦n⟧η) (inr ¬α≡⟦n⟧η) min≡₁
 = 𝟘-elim (¬α≡⟦n⟧η  k k<n  α≡⟦n⟧β k k<n  β≡⟦n⟧η k k<n))
discrete-seq-c'-ult α β η n (inl  α≡⟦n⟧β) (inr ¬β≡⟦n⟧α) (inr ¬α≡⟦n⟧η) min₁₀≡₁
 = 𝟘-elim (zero-is-not-one min₁₀≡₁)
discrete-seq-c'-ult α β η n (inr ¬α≡⟦n⟧β) (inl  β≡⟦n⟧α) (inr ¬α≡⟦n⟧η) min₀₁≡₁
 = 𝟘-elim (zero-is-not-one min₀₁≡₁)
discrete-seq-c'-ult α β η n (inr ¬α≡⟦n⟧β) (inr ¬β≡⟦n⟧α) (inr ¬α≡⟦n⟧η) min₀₀≡₁
 = 𝟘-elim (zero-is-not-one min₀₀≡₁)

…and this allows us to show that the discrete-sequence closeness function satisfies the four necessary properties.

discrete-seq-is-clofun : {X : 𝓤 ̇ }  (ds : is-discrete X)
                            is-clofun (discrete-seq-clofun ds)
is-clofun.equal→inf-close (discrete-seq-is-clofun ds) α
 = ℕ∞-equals  n  discrete-seq-c'-eic α n (discrete-decidable-seq ds α α n))
is-clofun.inf-close→equal (discrete-seq-is-clofun ds) α β cαβ≡∞
 = fe  n  discrete-seq-c'-ice α β n (discrete-decidable-seq ds α β n) (γ n))
 where
   γ : (n : )  discrete-seq-c' (α , β) n (discrete-decidable-seq ds α β n)  
   γ n = ap  -  pr₁ - n) cαβ≡∞
is-clofun.symmetricity    (discrete-seq-is-clofun ds) α β
 = ℕ∞-equals  n  discrete-seq-c'-sym α β n (discrete-decidable-seq ds α β n)
                                              (discrete-decidable-seq ds β α n))
is-clofun.ultrametric     (discrete-seq-is-clofun ds) α β η
 = λ n  discrete-seq-c'-ult α β η n (discrete-decidable-seq ds α β n)
                                     (discrete-decidable-seq ds β η n)
                                     (discrete-decidable-seq ds α η n)

We quickly note two corollaries needed for our main result.

Firstly, there is an obvious relationship between the closeness value c (α , β) : ℕ∞ and the equality of a prefix of α and β.

(Exercises for the reader:)

closeness→equality : {X : 𝓤 ̇ }  (ds : is-discrete X)
                    (α β :   X)  (n : )
                    (succ n )  discrete-seq-clofun ds (α , β)
                    α ≡⟦ n  β

equality→closeness : {X : 𝓤 ̇ }  (ds : is-discrete X)
                    (α β :   X)  (n : )
                    α ≡⟦ n  β
                    (succ n )  discrete-seq-clofun ds (α , β)

This relationship helps us to show that,

  if (     δ ↑) ≼ c (α , β)
then (succ δ ↑) ≼ c (x :: α , x :: β).
build-up : {X : 𝓤 ̇ }  (ds : is-discrete X)  (xs ys :   X)  (δ : )
          (δ )  discrete-seq-clofun ds (xs , ys)
          (x : X)
          (succ δ )  discrete-seq-clofun ds (x :: xs , x :: ys)
build-up {𝓤} {X} ds xs ys δ δ≼cxsys x
 = equality→closeness ds (x :: xs) (x :: ys) δ (γ δ δ≼cxsys)
 where
   γ : (δ : )  (δ )  discrete-seq-clofun ds (xs , ys)
      (x :: xs) ≡⟦ δ  (x :: ys)
   γ δ δ≼cxsys 0        *   = refl
   γ (succ δ) δ≼cxsys (succ k) k≤n = closeness→equality ds xs ys δ δ≼cxsys k k≤n

Secondly, by function extensionality, α ≡ (head α :: tail α).

head : {X : 𝓤 ̇ }  (  X)  X
head α   = α 0

tail : {X : 𝓤 ̇ }  (  X)  (  X)
tail α n = α (succ n)

head-tail-eta : {X : 𝓤 ̇ }  (α :   X)  α  head α :: (tail α)
head-tail-eta α = fe γ where
  γ : α  head α :: (tail α)
  γ 0 = refl
  γ (succ n) = refl

Continuity and continuously searchable types

Now that we have two examples of closeness functions, we show how they can be used to give a definition of uniform continuity that is related to the usual ε-δ definition on metric spaces.

A predicate p : predicate X on a type X with closeness function c : X × X → ℕ∞ is uniformly continuous if there is some δ : ℕ such that, for any x,y : X with (δ ↑) ≼ c (x , y), p(y) is inhabited if and only if p(x) is.

We call δ the uniform modulus of p on c.

_is-u-mod-of_on_ : {X : 𝓤 ̇ }    predicate X  (X × X  ℕ∞)  𝓤 ̇ 
_is-u-mod-of_on_ {𝓤} {X} δ p c = Π (x , y)  (X × X) , ((δ )  c (x , y)  p x  p y)

u-continuous : {X : 𝓤 ̇ }  (X × X  ℕ∞)  predicate X  𝓤 ̇
u-continuous {𝓤} {X} c p = Σ δ   , δ is-u-mod-of p on c

This allows us to define the notion of ‘continuously searchable’ types. These are types X with a closeness function c : X × X → ℕ∞ that allow us to search any uniformly continuous decidable predicate on X.

uc-d-predicate : (X : 𝓤 ̇ )  (X × X  ℕ∞)  (𝓤₀ )  𝓤 ̇
uc-d-predicate X c = Σ (p , d)  d-predicate X , u-continuous c p

c-searchable : (X : 𝓤 ̇ )  (X × X  ℕ∞)  (𝓤₀ )  𝓤 ̇
c-searchable X c = Π ((p , _)  , _)  uc-d-predicate X c , Σ x₀  X , (Σ p  p x₀)

Of course, any searchable type is trivially continuously searchable on any closeness function.

For example, 𝟚 is continuously searchable using the discrete closeness function.

c-searchable-types-are-inhabited : {X : 𝓤 ̇ }  (c : X × X  ℕ∞)  c-searchable X c  X
c-searchable-types-are-inhabited {𝓤} {X} c S = pr₁ (S trivial-predicate)
 where
   trivial-predicate : uc-d-predicate X c
   trivial-predicate = ((λ x  𝟙) ,  x  inl )) , (0 , λ x y _  )

searchable→c-searchable : {X : 𝓤 ̇ }  (c : X × X  ℕ∞)  searchable X  c-searchable X c
searchable→c-searchable c S ((p , d) , δ , ϕ) = S (p , d)

𝟚-is-discrete : is-discrete 𝟚
𝟚-is-discrete   = inl refl
𝟚-is-discrete   = inl refl
𝟚-is-discrete   = inr  ())
𝟚-is-discrete   = inr  ())

𝟚-is-c-searchable : c-searchable 𝟚 (discrete-clofun 𝟚-is-discrete)
𝟚-is-c-searchable
 = searchable→c-searchable (discrete-clofun 𝟚-is-discrete) 𝟚-is-searchable

Conversely, any discrete type that is continuously searchable by the discrete closeness function is also searchable: this is because all predicates on discrete types are uniformly continuous by this closenss function.

all-discrete-predicates-are-continuous
 : {X : 𝓤 ̇ }  (ds : is-discrete X)  d-predicate X
  uc-d-predicate X (discrete-clofun ds)
all-discrete-predicates-are-continuous {𝓤} {X} ds (p , d)
 = (p , d) , (1 , λ (x , y)  γ x y (ds x y))
 where
   γ : (x y : X)  (q : decidable (x  y))  (1 )  discrete-c' (x , y) q  p x  p y
   γ x .x (inl refl) 1≼∞ px = px
   γ x  y (inr  _  ) 1≼0 _  = 𝟘-elim (zero-is-not-one (1≼0 0 refl))

c-searchable-discrete→searchable : {X : 𝓤 ̇ }  (ds : is-discrete X)
                                  c-searchable X (discrete-clofun ds)  searchable X
c-searchable-discrete→searchable ds S (p , d)
 = S (all-discrete-predicates-are-continuous ds (p , d))

Main result

Now we come to the main result for this half.

We wish to show that, for any discrete X, ℕ → X is continuously searchable using the discrete-sequence closeness function.

→c-searchable : {X : 𝓤 ̇ }  (ds : is-discrete X)  c-searchable X (discrete-clofun ds)
               c-searchable (  X) (discrete-seq-clofun ds)

The proof here is by induction on the modulus of continuity of the predicate being searched. In order to convince the Agda synthesizer that this terminates, we prove the equivalent statement.

→c-searchable' : {X : 𝓤 ̇ }  (ds : is-discrete X)  searchable X
                ((p , d) : d-predicate (  X))
                (δ : )  δ is-u-mod-of p on (discrete-seq-clofun ds)
                Σ x₀  (  X) , (Σ p  p x₀)
               
→c-searchable ds S ((p , d) , δ , ϕ)
 = →c-searchable' ds (c-searchable-discrete→searchable ds S) (p , d) δ ϕ

The magic of this proof of course comes from continuity — we use two simple lemmas.

Lemma 1.

Any uniformly continuous discrete predicate p : uc-d-predicate X c, for any closeness function c : X × X → ℕ∞, with modulus of uniform continuity 0 : ℕ is satisfied by any construction of X.

0-mod-always-satisfied : {X : 𝓤 ̇ }  (c : X × X  ℕ∞)
                        ((p , d) : d-predicate X)
                        0 is-u-mod-of p on c
                        Σ p  Π p 
0-mod-always-satisfied c (p , d) ϕ (x₀ , px₀) x = ϕ (x₀ , x)  _ ()) px₀

trivial-predicate : {X : 𝓤 ̇ }  (c : X × X  ℕ∞)  uc-d-predicate X c
trivial-predicate c = ((λ _  𝟙) ,  _  inl )) , (0 , λ x y 0≼cxy  )

Lemma 2.

Given any uniformly continuous discrete predicate p : uc-d-predicate (ℕ → X) _, with modulus of uniform continuity (succ δ) : ℕ, we can construct the predicate (pₜ x) ≔ (λ xs → p (x :: xs)) : uc-d-predicate (ℕ → X) _, for any given x : X, which has modulus of uniform continuity δ : ℕ.

We call (pₜ x) the “tail predicate for p via x”.

tail-predicate : {X : 𝓤 ̇ }  (ds : is-discrete X)
                ((p , d) : d-predicate (  X))
                (x : X)  d-predicate (  X)
tail-predicate ds (p , d) x =  xs  p (x :: xs)) ,  xs  d (x :: xs))

tail-predicate-reduce-mod : {X : 𝓤 ̇ }  (ds : is-discrete X)
                           ((p , d) : d-predicate (  X))
                           (x : X)  (δ : )
                           (succ δ) is-u-mod-of p on (discrete-seq-clofun ds)
                                 δ  is-u-mod-of pr₁ (tail-predicate ds (p , d) x)
                                                   on (discrete-seq-clofun ds)
tail-predicate-reduce-mod {𝓤} {X} ds (p , d) x δ ϕ (xs , ys) δ≼cxsys
 = ϕ (x :: xs , x :: ys) (build-up ds xs ys δ δ≼cxsys x)

Given (pₜ x) for any x : X, we can construct the “head predicate” pₕ ≔ (λ x → x :: 𝓔xs x) : d-predicate X where 𝓔xs x : ℕ → X is the sequence that satisfies (pₜ x).

head-predicate : {X : 𝓤 ̇ }  (ds : is-discrete X)  searchable X
                ((p , d) : d-predicate (  X))
                (δ : )  (succ δ) is-u-mod-of p on (discrete-seq-clofun ds)
                d-predicate X
head-predicate {𝓤} {X} ds S (p , d) δ ϕ
 = ((λ x  p (x :: 𝓔xs x)) ,  x  d (x :: 𝓔xs x)))
 where
   𝓔xs : X  (  X)
   𝓔xs x = pr₁ (→c-searchable' ds S (tail-predicate ds (p , d) x)
           δ (tail-predicate-reduce-mod ds (p , d) x δ ϕ))

We now construct the searcher for the type ℕ → X by induction on the modulus of continuity of the predicate being searched.

Recall that in both cases we wish to construct some α : ℕ → X such that, if there is α₀ such that p(α₀) then also p(α).

When the modulus of continuity is 0, by Lemma 1 we can return any sequence for α. Because X is searchable, it is inhabited by some x : X, and so we simply set α ≔ (λ n → x).

→c-searchable' ds S (p , d) 0        ϕ
 = α , λ (x₀ , px₀)  γ (x₀ , px₀) α
 where
   x = searchable-types-are-inhabited S
   α = λ n  x
   γ : Σ p  Π p
   γ = 0-mod-always-satisfied (discrete-seq-clofun ds) (p , d) ϕ

When the modulus of continuity is (succ δ) : ℕ for some δ : ℕ, by Lemma 2 we can construct the tail predicate of p, which has modulus of continuity δ : ℕ, for any x : X — this predicate can be searched using the inductive hypothesis.

→c-searchable' {𝓤} {X} ds S (p , d) (succ δ) ϕ = α , γ where
  pₕ = pr₁ (head-predicate ds S (p , d) δ ϕ)
  pₜ = λ x'  pr₁ (tail-predicate ds (p , d) x')

Therefore, we can now search X for a solution to pₕ : d-predicate X, the head predicate of p, and use the inductive hypothesis to search ℕ → X for a solution to (pₜ x') : uc-d-predicate (ℕ → X) _, the tail predicate of p via any x' : X.

  S-head = S (head-predicate ds S (p , d) δ ϕ)

  IH-tail = λ x'  →c-searchable' ds S (tail-predicate ds (p , d) x')
                      δ (tail-predicate-reduce-mod ds (p , d) x' δ ϕ)

This gives us two constructions:

  1. x : X s.t. if there is x₀ such that pₕ(x₀) then also pₕ(x),
  x  : X
  x  = pr₁ S-head
  
  γₕ : Σ pₕ  pₕ x
  γₕ = pr₂ S-head
  1. 𝓔xs : X → (ℕ → X) s.t., given any x' : X, if there is xs₀ such that (pₜ x')(xs₀) then also (pₜ x')(𝓔xs x').
  𝓔xs : X  (  X)
  𝓔xs x' = pr₁ (IH-tail x')
  γₜ  : (x' : X)  Σ (pₜ x')  (pₜ x') (𝓔xs x') 
  γₜ  x' = pr₂ (IH-tail x')
We set α ≔ (x :: 𝓔xs x).
  α = x :: 𝓔xs x

  γ : Σ p  p α
  γ (α₀ , pα₀) = step₆ where

If there is some α₀ such that p(α₀), then also (by function extensionality) p(x₀ :: xs₀), where x₀ ≔ head α₀ and xs₀ ≔ tail α₀.

    x₀  = head α₀
    xs₀ = tail α₀
    
    step₁ : p (x₀ :: xs₀)
    step₁ = transport p (head-tail-eta α₀) pα₀

Therefore, by definition of pₜ, we have (pₜ x₀)(xs₀) and further, by construction of 𝓔xs, we also have (pₜ x₀)(𝓔xs x₀).

    step₂ : (pₜ x₀) xs₀
    step₂ = step₁
    
    step₃ : (pₜ x₀) (𝓔xs x₀)
    step₃ = γₜ x₀ (xs₀ , step₂)

Note that (pₜ x₀)(𝓔xs x₀) ≡ p(x₀ :: 𝓔xs x₀) ≡ pₕ. Therefore, by definition of pₕ, we have pₕ(x₀) and further, by construction of x, we also have pₕ(x).

    step₄ : pₕ x₀
    step₄ = step₃
    
    step₅ : pₕ x
    step₅ = γₕ (x₀ , step₄)

Note that pₕ(x) ≡ p (x :: 𝓔xs x), giving us our conclusion.

    step₆ : p (x :: 𝓔xs x)
    step₆ = step₅

A corollary to this theorem, of course, is that the Cantor space is continuously searchable.

ℕ→𝟚-is-c-searchable : c-searchable (  𝟚) (discrete-seq-clofun 𝟚-is-discrete)
ℕ→𝟚-is-c-searchable = →c-searchable 𝟚-is-discrete 𝟚-is-c-searchable

But we still have to prove the full blown Tychonoff theorem using closeness functions and continuously searchable types. Have a think about how we can define a closeness function on an infinite series of types T : ℕ → 𝓤, where each (T n) : 𝓤 has a closeness function.

Once you’ve had a think please click here to read Part II, where we formalise the Tychonoff theorem for continuously searchable types.