Undecidable

How can we prove results about undecidability in a setting where everything terminates?

{-# OPTIONS   #-}
open import Level using (0ℓ)
open import Data.Nat
open import Data.Empty
open import Data.Nat.Properties using (≤⇒≤′; ≤′⇒≤)
open import Data.Product
open import Data.Maybe
open import Relation.Binary.PropositionalEquality
open import Relation.Unary
open import Relation.Nullary using (¬_; Dec; yes; no; contradiction; map′)
open import Function.Base
import Data.Nat.Properties as 


open import Partial


module _ where
private
  variable
    A B C : Set

Models of Computation

record model-of-computation (A : Set) : Set where
  field
    run : (code : )  (input : A)  Step-Indexed A

  T : (code : )  (input : A)    Maybe A
  T c x k = Step-Indexed.f (run c x) k 

We will only use A = ℕ later, but keep the A to distinguish between numbers "here" in the metalanguage and the encoded values.

We define enumerators of predicates P : ℕ → Set as functions that yield just x for some input iff x ∈ P. For its definition, we define the image of partial functions:

Im[_] :  (f : A  B)  B  Set
Im[_] f b = ∃[ a ] f a  just b

Enumerable : (  Set)  Set
Enumerable P = Σ[ f  (  Maybe ) ] P  Im[ f ]

_≈im_ : (f g : A  Maybe B)  Set
f ≈im g = Im[ f ]  Im[ g ]

The last relation describes that two functions enumerate the same predicate, regardless of the order.

Pairing: ℕ ≅ ℕ × ℕ

For encoding A record for bijections between sets

infix 1 _≅_
record _≅_ (A B : Set) : Set where
  field
    to : A  B
    from : B  A
    to∘from : (to  from)  id {_} {B}
    from∘to : (from  to)  id {_} {A}

The isomorphism ℕ ≅ ℕ × ℕ constructs following sequence of pairs:

 0,3
     ↖
 0,2   1,2
     ↖     ↖
 0,1   1,1   2,1
     ↖     ↖     ↖
 0,0   1,0   2,0   3,0

The diagonal with index k contains the elements

{ (x , y) ∈ ℕ × ℕ ∣ x + k ≡ k }

Given a pair, the following function constructs the next pair in above sequence:

next-pair :  ×    × 
next-pair (zero , y) = suc y , 0
next-pair (suc x , y) = x , (suc y)

unpair :    × 
unpair zero = zero , zero
unpair (suc k) = next-pair (unpair k)

-- how many elements before diagonal number k
elements-before :   
elements-before zero = 0
elements-before (suc k) = elements-before k + suc k

pair :     
pair x y = elements-before (x + y) + y

postulate
  unpair∘pair :  x y  unpair (pair x y)  (x , y)
  pair∘unpair :  k  (uncurry pair) (unpair k)  k

ℕ≅ℕ×ℕ :    ×  
ℕ≅ℕ×ℕ = record
  { to = unpair
  ; from = uncurry pair
  ; to∘from = uncurry unpair∘pair
  ; from∘to = pair∘unpair
  }

Maybe ℕ ≅ ℕ

Maybeℕ≅ℕ : Maybe    
Maybeℕ≅ℕ = record
  { to = λ { (just x)  suc x ; nothing  0 }
  ; from = λ { zero  nothing ; (suc x)  just x }
  ; to∘from = λ { zero  refl ; (suc x)  refl }
  ; from∘to = λ { (just x)  refl ; nothing  refl}
  }

Truncation

With a prefix ., we can describe "proof irrelevance". Intuitively, these are statements whose value are irrelevant for the actual computation.

record ∥_∥ (A : Set) : Set where
  constructor ∣_∣
  field
    .proof : A

trunc : {A : Set}  A   A 
trunc a =  a 

∥⊥∥→⊥ :     
∥⊥∥→⊥ ()

∥map : (A  B)   A    B 
∥map f  x  =  f x 

¬trunc : {A : Set}  ¬ A  ¬  A 
¬trunc ¬A = ∥⊥∥→⊥  ∥map ¬A

module example where
  test-g : (n : )  .(n  1)  (2 + ((1 + n)  2)  (1 + n))
  test-g (suc n) _ = refl

  test-f : (n : )   n  2   (2 + (n  2)  n)
  test-f (suc zero) 1≥2 = ⊥-elim (¬trunc  { (s≤s ())}) 1≥2) 
  test-f (2+ n) g = refl

[End of lecture on 13 July 2026]

Undecidability of the Halting Problem

Church's Thesis CT describes that every endofunction f : A → A can be computed by some programme code c:

_↓_ : Step-Indexed B    Maybe B
_↓_ = Step-Indexed.f

-- a machine M computes a function
_~~>_ : (A  Step-Indexed B)  (A  B)  Set
M ~~> f =  x  ∃[ k ] M x  k  just (f x)


module Undecidable-Halting (Model : model-of-computation ) where
  open model-of-computation Model
  module assume-CT (CT : (f :   )   (Σ[ c   ] run c ~~> f) ) where

The halting problem decides for a code c, whether the machine number c halts when started with input c.

    -- the property that machine number c halts on some input x
    _halts-on_ :     Set
    c halts-on x = ∃[ v ] ∃[ k ] run c x  k  just v
    Halting :   Set
    Halting c = c halts-on c

The halting problem decides for a code c, whether the machine number c halts when started with input c.

    Halting-undecidable : ¬ Decidable Halting
    Halting-undecidable Halting? = contradiction (CT f) (¬trunc f-has-no-code)
      where

In order to prove its undecidability, we define a function f that cannot have any code:

  f : ℕ → ℕ
        ⎧ suc(v)  if the machine c, started with input c, halts
  f c = ⎨         with output v
        ⎩ 0       otherwise
      f :   
      f c = case (Halting? c) of λ
        { (yes (v , k , _))  suc v
        ; (no _)  0
        }

If the function f was computed by some machine F, then f F returns suc(v) where v is the computation result of F started with F, so v = f F, a contradicton.

      f-prop :  (G : ) (g :   )  (run G) ~~> g
                f G  suc (g G)
      f-prop G g G~~>g with (Halting? G)
      ... | yes (v , k , G[G]↓k≡just[v]) =
            -- if G halts with result v
            -- then this result v must be `g G`
            cong suc (Step-Indexed-functional (refl {_} {_} {run G G})
                 G[G]↓k≡just[v] (proj₂ (G~~>g G)))
      ... | no G[G]↑ =
            -- it is impossible that G does not halt,
            -- because G computes g, so it must halt
            -- on every input, in particular on input `G`
            contradiction ((g G) , (G~~>g G)) G[G]↑

      f-has-no-code : ¬ (Σ[ F   ] run F ~~> f)
      f-has-no-code (F , F~~>f) = ℕ.1+n≢n (sym (f-prop F f F~~>f))

The general halting problem is then also undecidable:

    open import Relation.Binary renaming (Decidable to Decidable₂)
    GeneralHalting-undecidable : ¬ Decidable₂ _halts-on_
    GeneralHalting-undecidable _halts-on?_ = Halting-undecidable  c  c halts-on? c)

Untruncated Church's Thesis CTΣ

module Untruncated-vs-Extensionality (Model : model-of-computation ) where
  open model-of-computation Model
  open import Axiom.Extensionality.Propositional using (Extensionality)
  module assume-CTΣ (CTΣ : (f :   )  (Σ[ c   ] run c ~~> f)) where

    ⟪_⟫ : (  )  
     f  = proj₁ (CTΣ f)

    ⟪_⟫[_]-correct : (f :   )  (x : )  (run  f  x  _  just (f x))
     f ⟫[ x ]-correct = proj₂ (proj₂ (CTΣ f) x)

    module assume-extensionality (functional-extensionality : Extensionality 0ℓ 0ℓ) where
      opaque
        _≗?_ : (f g :   )  Dec (f  g)
        f ≗? g =
          -- we reduce their point-wise equality to equality of their codes
          map′
          -- if their codes are the same, they return the same
          -- step-indexed values, and so return the same
          -- (since both halt)
           ⟪f⟫≡⟪g⟫ x 
             Step-Indexed-functional
               ((cong  -  run - x)) ⟪f⟫≡⟪g⟫)
                f ⟫[ x ]-correct
                g ⟫[ x ]-correct)
          -- if the functions are point-wise equal, then
          -- they are identical (by extensionality) and
          -- thus must have the same code.
           f≗g  cong ⟪_⟫ (functional-extensionality f≗g))
          ( f    g )

      try-run :     
      try-run c k = fromMaybe 0 (run c c  k)

      to01 : {P : Set}  Dec P  
      to01 (yes _) = 1
      to01 (no _)  = 0

      zeros? :   
      zeros? c = to01 (try-run c ≗? const 0)

      Z : 
      Z =  zeros? 

      open ≡-Reasoning
      inconsistent : 
      inconsistent with (try-run Z ≗? const 0) in dec-result
      ... | yes try-run[Z]≗const[0] =
          ℕ.0≢1+n (begin
          0                             ≡⟨ try-run[Z]≗const[0] _ 
          try-run Z _                   ≡⟨⟩
          fromMaybe 0 (run Z Z  _)     ≡⟨ cong (fromMaybe 0)  zeros? ⟫[ Z ]-correct 
          fromMaybe 0 (just (zeros? Z)) ≡⟨⟩
          zeros? Z                      ≡⟨ cong to01 dec-result 
          1
          )
      ... | no try-run[Z]≢const[0] = try-run[Z]≢const[0] try-run[Z]≗const[0]
        where
          try-run[Z]≗const[0] : try-run Z  const 0
          try-run[Z]≗const[0] k with (run Z Z  k) in Z[Z]↓k≡just[v]
          ... | nothing = refl
          ... | just v =
                -- we need to show v ≡ 0
                (v  0)  (Step-Indexed-functional
                -- run Z Z returns v in the present case:
                (refl {_} {_} {run Z Z})
                -- run Z Z returns 0 by CTΣ:
                ((run Z Z  _  just v)  Z[Z]↓k≡just[v])
                (begin
                  (run Z Z  _)     ≡⟨  zeros? ⟫[ Z ]-correct 
                  just (zeros? Z)   ≡⟨ cong just (cong to01 dec-result) 
                  just 0
                  ))

      real-inconsistency :  {} (A : Set )  A
      real-inconsistency = ⊥-elim inconsistent

[End of lecture on 16 July 2026]

Literature

Agda Quellcode herunterladen