The Universal Function
From this point onwards let us fix a particular variable x
wrt which our
definition of
computability
is stated.
We define a function
We often call
for all
The following is a celebrated result in computability theory:
Theorem (Kleene). The universal function is computable.
Let’s unpack what this means. First things first: in order to speak of
computability, we have to encode the domain as natural numbers. The domain
itself is the product of two sets,
where if
It is not very difficult to prove that if
It follows that we can reflect this function into the natural numbers using the Gödel numbering and pairing.
The theorem says that resultant function is computable!
In lieu of a proof, let us informally sketch what the program that computes this does.
- Upon receiving
as an argument, it decodes it as a pair . - It decodes the first component
into (the AST of) a While program . - It simulates the running of the program
on input .
This last step consists of constructing the configuration
That description may seem familiar: it corresponds to what we would nowadays call an interpreter for a programming language. An interpreter is a program that loads the source code of another program, and computes the intended behaviour of that program. One way to write an interpreter is to follow the operational semantics of a language, as described above. Usually interpreters do not translate the source code, but merely produce the desired behaviour directly. The most famous language that is interpreted (and not compiled) is perhaps Python.
This theorem of Stephen Kleene states that it is possible to write an interpreter. Curiously, it was proven long before any actual computers existed!
The proof of the theorem proceeds by actually constructing the interpreter
itself as a While program. We will not do so, because it is awfully tedious:
it involves unpairing a number of inputs, and pattern-matching on the next
instruction (if the program is of the form
However, it should not be very hard for you to see how to write such an interpreter in Haskell, using the powerful features of algebraic data types and pattern-matching.
For the purposes of this unit we are willing to accept Kleene’s theorem at face value, without an explicit construction. We will also be reasonably relaxed about the explicit construction of such programs from this point onwards, and accept handwaving instead.