2.3 Equality versus atomic equality
2.3.1 More syntactic sugar
Rewrite rules: The logic of the nestedifcommands above is not easy to follow (one has to parenthesize them). A convenient more compact form is to write nestedif’s as a sequence of rewrite rules of form rewrite [X1, X2,...,Xn] by Rule1;...;Rulem. Here eachXiis a variable, and a ruleRulejmay have one of two forms:
1. [pat1,...,patn]⇒[E1,...,En], or 2. [pat1,...,patn]⇒C;
where eachpatiis apatternbuilt from new variables usingniland the tree constructor “.” andCis a command.
Informal semantics: if the current values of variablesX1, X2,...,Xnmatch patterns pat1,...,patn(in left-to-right order), then the rule is applied. If the rule has the first format, Eiis an expression assigning a new value to variable Xi. If the second, C is a command that may changeXi. The right-side expressionsE1,. . . ,Enor commandCmay contain references to variables appearing in the patterns, though not to the left of:=.
read X; GO := true; Y := false; while GO do rewrite [D, E] by [((D11.D12).D2), ((E11.E12).E2)]⇒[(D11.(D12.D2)), (E11.(E12.E2))] [ ((D11.D12).D2), (nil.E2) ] ⇒ GO := false; [ ((D11.D12).D2), nil ] ⇒ GO := false; [ (nil.D2), ((E11.E12).E2) ] ⇒ GO := false; [ (nil.D2), (nil.E2) ] ⇒ [ D2, E2 ] [ (nil.D2), nil ] ⇒ GO := false; [ nil, (E1.E2) ] ⇒ GO := false;
[ nil, nil ] ⇒ Y := true; GO := false;
write Y;
Such rules are easily expanded into nested if commands. For instance, the first rule would naturally expand into:
if D then
if (hd D) then if E then
if (hd E) then
D := cons (hd (hd D)) (cons (tl (hd D)) (tl D)); E := cons (hd (hd E)) (cons (tl (hd E)) (tl E))
and the next-to-last rule would expand to: if D then skip else if E then if (hd E) then skip else GO := false
Thecase statement: A similar construction to aid readability is thecasestatement, with syntax
case E of pat1 ⇒ C1; ...
Again, this expands into a sequence of nestedifstatements, and the commandsC1,. . . ,Cn may contain references to variables appearing in the patterns (though not to the left of :=).
Exercises
2.1 Write a WHILE program that takes an input d and returns the list of atoms ind from left to right. For instance, withd=((a.b).(a.(c.d))the program should yield(a
b a c d)(i.e.,(a.(b.(a.(c.(d.nil)))))). 2
2.2 Write a WHILE program that expects an input of the form (d1· · · dn) (a list of
values), and removes adjacent occurrences of the atom nil. For instance, if the input is (nil (nil) nil nil ((nil)) nil)), the program should yield ((nil (nil) nil
((nil)) nil))). 2
2.3 Letσ={X7→(nil.nil)},Cbewhile X do X:=X, and show that there is noσ0 such
thatC`σ→σ0. 2
2.4 Givend=(a b c), and letp=read X; C; write Ybe the reverse program from Subsection 2.1.3. Find aσ such thatC`σp0→σ. Explain in detail howσ is computed.
2
2.5 Prove that [[reverse]](d1· · ·dn) = (dn· · ·d1). Hint: Proceed by induction onn.5 2 2.6 * This concerns the general program for testing equality in section 2.3. Consider the weight functionw: ID→IN defined by:
w(d) = |d| −r(d) where r(nil) = 1
r((d1.d2)) = 1 +r(d2) r(d) = length of right spine ofd
Exercise: First, argue that this function decreases in each loop of the equality-testing program of section 2.3. Then find an upper bound on the running time of the equality- testing program.
2.7 Prove that the size |d| of a value d∈ID can be computed in time O(|d|). Hint: modify the program for testing equality in section 2.3, so it compares d against itself, and increases a counterniln each time a new “.” is found ind. 2
References
The data structure of WHILE is very similar to those of Scheme and LISP. The book by Kent Dybvig [41] is a good introduction to Scheme. The semantics of the WHILE language is in essence a natural semantics as one would find it in an introductory text on programming language semantics, e.g., the books by Schmidt [158] or by Nielson and Nielson [136].
Some other textbooks on computability and complexity use a language very similar to WHILE, but in most cases the data structure used is numbers, rather than trees [97, 164]. The author has used structured data as well as structured programs for teaching for several years at Copenhagen. The idea of restricting trees to the single atom nilwas due to Klaus Grue [57]. The same WHILE language was used in article [78], which contains several results and definitions appearing later in this book.
In this chapter we are concerned with programs that take other programs as data. This requires that programs be part of the data domain; we show how to achieve this in Section 3.2. We then study three kinds of programs that have other programs as input in Sections 3.3–3.6: compilers, interpreters, and specializers. The chapter concludes with several simple examples of compilation in Section 3.7.
Acompileris a program transformer which takes a program and translates it into an equivalent program, possibly in another language. An interpreter takes a program and its input data, and returns the result of applying the program to that input. Aprogram specializer, like a compiler, is a program transformer but with two inputs. The first input is a programpthat expects two inputsX,Y. The other input to the program specializer is a value sfor X. The effect of the specializer is to construct a new programps which
expects one inputY. The result of runningps on input d, is to be the same as that of
runningpon inputssandd.
The reason we emphasize these program types is that many proofs in computability theory involve, either explicitly or implicitly, constructing an interpreter, a compiler, or a specializer.
First we define what constitutes a programming language in Section 3.1.
3.1
Programming languages and simulation
Definition 3.1.1 Aprogramming language Lconsists of 1. Two sets,L−programsandL−data;
2. A function [[•]]L:L−programs→(L−data→L−data
⊥)
Here [[•]]LisL’ssemantic function, which associates with everyL-programp∈L−programs
a corresponding partial function [[p]]L:L−data→L−data
⊥.
If L−programs⊆L−data, we will henceforth say thatL hasprograms-as-data. Also, ifL−data×L−data⊆L−data, we will say thatLhaspairing. 2 We have already seen one example of a programmming language according to this defi- nition, viz. the language WHILE, which hasL−data= ID andL−programs as in Defini- tion 2.1.3. WHILE has pairing (the “cons” operator on ID), and we will soon see how to represent WHILE-programs as values in ID.
More programming languages will be seen in later chapters. As was the case for WHILE, we will dropLfrom the notation [[•]]LwheneverLis clear from the context.
Imagine one has a computer with machine language M. How is it possible to run programs written in another languageL? We will answer this question in two steps. First, we say what it means for languageMto be able to simulate an arbitraryLprogram. (In effect, this saysMis at least asexpressiveasL.) Second, we will showhowMcan simulate L, in two different ways: compilation and interpretation.
Definition 3.1.2 SupposeL-data=M-data. Language Mcan simulatelanguage Lif for everyp∈L-programsthere is anm-programqsuch that for alld∈L-datawe have
[[p]]L(d)'[[q]]M(d)
Equivalently: M can simulateL iff there is a total functionf :L-programs→M-programs
such that [[p]]L= [[f(p)]]Mfor allL-programsp.
Language Lis equivalent tolanguage M, writtenL≡M, if language Land languageM
can simulate each other. 2
This definition expresses the facts thatLandMcan compute the same functions; but it does not assert the existence of anyconstructiveway to obtain anM-program equivalent to a givenL-program. The remainder of this chapter concerns how simulation may be done computably, by either translation (applying a compiling function) or by interpretation. First, however, we will need a way to regard programs as data objects.
3.2
Representing
WHILE
programs in
ID
We have earlier given a syntax forWHILE-programsandWHILE-data. Suppose we want to give a WHILE program as input to another WHILE program. Presently this is not possi- ble simply because elements ofWHILE-programsare not objects inWHILE-data. Therefore we now give aprograms-as-data representation for WHILE programs.
Definition 3.2.1 Let {:=, ;, while, var, quote, cons, hd, tl, =?, nil} denote 10 distinct elements of ID. The representationpof WHILE programpis defined by the map shown in Figure 3.11:
•:WHILE−programs→WHILE−data 1Recall thatVars={V
0,V1, . . .}. While we often useXandYto denote arbitrary elements ofVars, it
is convenient in the definition of•to know the index of the variable to be coded. We assume that no program contains a variable with higher index than its output variable.
where we use the list and number notation of Subsections 2.1.5–2.1.6. 2
read Vi; C; write Vj = ((vari)C(varj))
C;D = (;CD) while E do C = (whileEC) Vi:=E = (:= (vari)E) Vi = (vari) d = (quoted) cons E F = (consEF) hd E = (hdE) tl E = (tlE) =? E F = (=?EF)
Figure 3.1: Mapping WHILEprograms to their data representations.
For example, ifXandYare the variablesV1andV2, respectively, then the program written as read X; Y := nil; while X do Y := cons (hd X) Y; X := tl X write Y;
would be translated to the value in ID: (
(var 1)
(; (:= (var 2) (quote nil)) (while (var 1)
(; (:= (var 2) (cons (hd (var 1)) (var 2))) (:= (var 1) (tl (var 1))))))
(var 2) )
For readability we will continue to use the original syntax when writing programs, but it should be understood that whenever a programp is input to another, it is the corre- sponding representationpthat we have in mind.
Analogous ideas can be used for other languagesLas well, though encoding programs as data is harder if L-data is, as in classical computability texts, the set of natural numbers.
3.3
Compilation
Suppose we are given three programming languages:
• A source languageS,
• A target languageT, and
• Animplementation languageL.
A compiler comp ∈ L-programs from S to T has one input: a source program p ∈
S-programs to be compiled. Running the compiler with input p (on an L-machine)
must produce another program target, such that running targeton a T-machine has the same effect as runningpon anS-machine.
This is easiest to describe (and do) if the source and target languages have the same data representations S-data =T-data, as one can simply demand that [[source]]S(d)'
[[target]]T(d) for all inputsd.