One of the problems that arises in programming is that one can make a mistake in writing a program and then, when the incorrect procedure called, it not only does not return the correct result, it just never returns. In this situation we usually say the the procedure call has entered an "infinite loop", but we mean simply that the program runs and runs forever, without ever returning a result.
Consider, for example, the following incorrect implementation of the countdown
(define (c N)
(if (= N 0) 'done
(c (- 1 N))))
Here we meant to have the recursive call be
(c (- N 1)), but we mistakenly
interchanged the arguments to the subtraction operator, so instead of subtracting 1 from N
it will now subtract N from 1.
Lets trace this procedure for a few inputs.
(c 0) --> done
(c 1) --> (c (- 1 1)) --> (c 0) --> done
(c 2) --> (c (- 1 2)) --> (c -1) --> (c (- 1 -1)) --> (c 2) --> (c -1) --> (c 2) --> ....
Clearly, the call
(c 2) will never return since it has entered an infinite loop.
It would be nice to have a new primitive in Scheme
(halts P A) which would
return true if the call
(P A) will eventually halt with an answer (or because of an error),
and will return false if the call
(P A) never halts and never has an error.
For example, we would like to do the following:
> (define (c N) (if (= N 0) 'done (c (- N 1)))) define > (halts c 5.0) #t ;; as (c 5.0) --> (c 4.0) --> ... --> (c 0.0) done > (halts c 1234567890) #t > (halts c -1) #f ;; as (c -1.0) --> (c -2.0) --> .... never reaches 0 > (halts c c) #t ;; it halts because it returns an error! ;; (c c) --> (c (- c 1)) --> ERROR > (define (f g) (g -1)) g > (halts f c) #f ;; because (halts f c) -> (c -1) -> infinite loop > (define (h x) (if (equal? x 0) 1 (h x))) h > (halts h h) #fIf we had such a program and if it always returns either true or false, then we could build an interpreter which would always call the halts method at the same time as it runs a user's query. If the answer comes back as false, then it could stop the program and tell the user they are in an infinite loop.
Alas, it is impossible to write such a procedure, and we will explain why below.
We now show that it is impossible to build such a primitive into Scheme. We will prove this by imagining that we could have such a procedure and showing that such an assumption leads to absurd results and so can't be true.
Lets assume that Ms. Jane Skeem has developed a version of Scheme with a new
halts such that
given a procedure
P and an input A
(halts P A)
returns true if (P A) eventually halts (with an answer or an error),
and false if (P A) never halts. We could then write the following
(define (sceptic Q)
(if (halts Q Q) (sceptic Q) 'ha))
Thus, for example, with the procedures defined above we find
(sceptic c) goes into an infinite loop:
> (sceptic c) --> (if (halts c c) (sceptic c) 'ha) --> (if #t (sceptic c) 'ha) --> (sceptic c) --> (if (halts c c) (sceptic c) 'ha) --> ... infinite loopwhile
> (sceptic h) --> (if (halts h h) (sceptic h) 'ha) --> (if #f (sceptic h) 'ha) --> 'haAlso observe that for any scheme term x, the expression
(sceptic x)either returns with "ha" or goes into an infinite loop.
Lets see what happens if we call this procedure on itself!!
In particular, does
(sceptic sceptic) halt or loop?
|NOTE: We are assuming throughout this discussion that it is possible to write such a "halts" procedure. We will discover that it is not possible, in much the same way that it is not possible for a person to tell the future in precise detail.|
(sceptic sceptic) -->
(if (halts sceptic sceptic) (sceptic sceptic) 'ha)
There are two cases:
(sceptic sceptic) --> (if (halts sceptic sceptic) (sceptic sceptic) 'ha) --> (sceptic sceptic) --> (if (halts sceptic sceptic) (sceptic sceptic) 'ha) --> (sceptic sceptic) --> ....But we can see that this obviously leads to an endless loop, so (sceptic sceptic) can not halt. Thus, the test (halts sceptic sceptic) cannot be true.
(sceptic sceptic) --> (if (halts sceptic sceptic) (sceptic sceptic) 'ha) --> 'haBut this obviously halts, so the test (halts sceptic sceptic) cannot be false.
You might object that this is just a little trick and that maybe we could
write such a procedure but that it just couldn't be applied to programs that
contain the "halts" primitive. Alas, this restriction does not help. Indeed,
we could easily extend Scheme with "reflection" primitives that give for
any procedure P its defining code (as a quoted list) and a list of all the
symbols in P and their meanings (the so-called environment of P). We could then
try to write the
halts procedure in Scheme itself, if this were
possible, then the same "sceptic" example would show that our "halts" procedure
would have to be wrong for at least some programs.
The rigorous proof of the unsolvability of the halting problem is somewhat more complicated because it very carefully defines the programming language in terms of a simplified model of computers called Turing machines and then shows that there can be no Turing machine program which will determine whether or not any given Turing machine program P will halt or loop on any given input I.
This shows that there is a problem that no computer can solve!
There are in fact many problems, some of them which seeminly have nothing to do with computers, that are also unsolvable by computers. One famous problem is Hilbert's 10th problem concerning the solution of so-called Diophantine equations:
Given an equation P(x1,x2,...,xn)=0, where P is a polynomial in several variables with integer coefficients, determine whether there are any integers a1,...,an such that P(a1,a2,...,an)=0, i.e. whether it has any integer solutions.This was proved by showing that if it could be solved by a computer, then a computer could use it to solve the Halting problem!