Computability Theory (i): the Halting Problem.
October 19, 2022 · 4 mins · 857 words
During these last few days I’ve been reading a little bit about computability theory, and I feel like a kid with a new toy, so I’m going to write some posts about this topic. I don’t pretend to explain anything new, and probably what I’m going to write has been written before, but I’ll write these posts for two reasons: (1) as future notes for myself, and (2) to help me clarify and organize the ideas.
This post has been inspired by this course and this post. So if your time is limited, I recommend you spend it going through the above links instead of my blog post.
The halting problem
To start let me define the Halting problem.
Given an arbitrary program, determine if it will finish running or continue to run forever.
If the program eventually finishes running we say that the program halts.
For the original paper by Alan Turing please go here.
Is there a solution to the halting problem?
We’re going to show that a program that determines if another program will halt can’t exist using a proof by contradiction. I’m going to use python
syntax to help me with the proof, but this can be generalized to any computer language.
Let’s start with the assumption that you can write a program halts
def halts(program: str, args) -> bool:
...
such that given an input program
and some args
returns True
if program(args)
halts and False
if the program continues to run forever.
For example, if
program = """
def foo(arg):
print(arg)
"""
arg = "Hello, world"
Then halts(program, arg)
would return True
, since the program halts after printing Hello, world
. However, if we define
program = """
def bar(arg):
while True:
pass
"""
arg = None
Then halts(program, arg)
would return False
, since the program will be stuck forever in the while loop.
Now, let’s make things a little bit more complicated and define the function g
as
def g(program: str):
if halts(program: str, program: str):
while True: # loop forever
pass
this is, if program(program)
halts then g
will loop forever, and if program(program)
doesn’t halt, then g
will halt. So basically, g
has the opposite behaviour of program(program)
.
Let’s go a little bit meta now. What happens if we get the string representation of the program g
and pass it to g
? This is, we want to know the behaviour of the following piece of code
g_str = """
def g(program: str):
if halts(program: str, program: str):
while True: # loop forever
pass
"""
g(g_str)
To know what happens we first need to know the output of halts(g_str, g_str)
. We know that halts
only returns True
or False
. Let’s assume first that it returns True
. Then, g(g_str)
will loop forever. But then halts
would be wrong!
Since halts(g_str, g_str)
can’t return True
, let’s assume then that it returns False
. Then it means that g(g_str)
will not halt, but according to the definition of g
it will halt. So halts
is again wrong.
WOW!! What does that mean? This means that the method g
can’t be constructed, and since g
has been constructed only under the assumption that halts
exists, it means that halts
can’t exist! The method halts
has at least one bug. This means that the original problem is unsolvable since for any implementation of halts
you build we can build a program g
that breaks your implementation. Therefore
The halting problem is unsolvable!
Conclusion
Probably, this proof is not rigorous enough from a mathematical point of view, but it has helped me a lot to understand the basics of the halting problem and its solution. Over the years I heard about the halting problem a lot of times in a lot of different contexts, but I never had an intuition about it and why it’s important. For me, it was just a weird problem that only mathematicians care about. However, after going over the problem and getting some intuition I think I start to understand its implications. In the following posts, I’ll show some “practical” applications of this proof, aka uncomputable numbers.