The Markov's principle states that if a Turing machine does not run forever, then it halts. Equivalently, if I have a function $f : \mathbb N \to \mathrm{Bool}$, such that I have proved that $\neg\neg \exists n. f(n)=\mathrm{true}$, then I can find $\exists n. f(n)=\mathrm{true}$.
This principle is sometimes considered as constructively valid, since we can give a program that just enumerates $\mathbb N$ and searches for the solution, and if our meta-logic is classical, we can see that this program halts, which means that it is constructively true for each concrete function $f$.
What are the best practices to incorporate this principle into various proof assistants? The most naïve approach is, of course, assume it as an axiom. But I would also like to have computational properties. So for each concrete function $f$, I can do this in Agda:
f : Nat -> Bool
f = blah blah
f-good : ¬ ¬ ∃[ x ∈ Nat ] f x ≡ true
f-good = blah blah
private
{-# TERMINATING #-}
helper : Nat -> ∃[ x ∈ Nat ] f x ≡ true
helper n with f n in eq
... | true = n , eq
... | false = helper (suc n)
magic : ∃[ x ∈ Nat ] f x ≡ true
magic = helper 0
I have to seal helper
as private, because it indeed does not terminate on some input. But given f-good
, we can be (meta-classically) sure that magic
will halt.
However, this has some drawbacks:
- If the normalization strategy is wrong, then this program will not terminate. For instance, if Agda chose to first fully expand all the branches before picking one, then it will keep entering the
false
branch. - If I try to state the general Markov's principle instead of on concrete $f$'s, then Agda will indeed loop in certain cases.
- I might accidentally make some unsafe leaks.
Is there any better way of doing this? Also, is there any proof assistant that actually supports this with a flag or something?