Academia.eduAcademia.edu

A combinatory logic model of programming languages

1974

A COMBINATORY LOGIC MODEL OF PROGRAMMING LANGUAGES by S. Kamal Abdali A thesis submitted in partial fulfillment of the requirements for the degree of DOCTOR OF PHILOSOPHY (Computer Sciences) at the UNIVERSITY OF WISCONSIN 1974 ACKNOWLEDGMENT I would like to thank Professor George Petznick for the advice, guidance, and constructive criticism I received from him during the research for this thesis. I am also grateful to Professors Donald Fitzwater, Lawrence Landweber, and Edward Moore for being on my examination committee and offering valuable suggestions. My special appreciation goes to Professor Tad Pinkerton for helping me during some desperate moments. I also acknowledge my indebtedness to the Courant Institute, where most of this work was done, for a stimulating research environment, and to the AEC Computing Center, for the computing facilities used in the experimental verification of parts of this model. ii A COMBINATORY LOGIC MODEL OF PROGRAMMING LANGUAGES S. Kamal Abdali Under the supervision of Assistant Professor George W. Petznick A simple correspondence is presented between a large subset of the ALGOL 60 language and the combinatory logic. With the aid of this correspondence, a program can be translated into a single combinatory object. The combinatory object representing a program is specified, in general, by means of a system of reduction relations among the representations of the program constituents. This object denotes, in terms of the combinatory logic, the function that the program is intended to compute. The model has been derived by using intuitive, functional interpretations of the constructs of programming languages, completely avoiding the notions of machine command and address. In particular, the concepts of program variable, assignment, and procedure have been accounted for in terms of the concepts of mathematical variable, substitution, and function, respectively. High-level programming language features are represented in the combinatory logic directly, not in terms of the representations of machine-level operations. Input-output is treated in such a manner that when the representation of a iii program is applied to the representations of the input items, the resulting combination reduces to a tuple of the representations of the output items. The applicability of the model to the problems of proving program equivalence and correctness is illustrated by means of examples. Approved by George W. Petznick (signed) iv CONTENTS Page Chapter 1. Introduction 1 1.1 Preliminary Remarks 1 1.2 Background 5 Chapter 2. The Combinatory Logic 11 2.1 Morphology and Transformation Rules 11 2.2 Functional Abstraction 18 2.3 The Lambda-Calculus 31 2.4 Additional Obs 38 Basic Programming Features 57 3.1 An Overview 57 3.2 Constants, Operations, Relations 60 3.3 Variables 64 3.4 Expressions 71 3.5 Assignments 76 3.6 Compound Statements 82 3.7 Blocks 86 3.8 Input-Output 91 3.9 Programs 94 3.10 Conditional Statements 97 3.11 Arrays 99 Chapter 3. v Chapter 4. Iteration and Jump Statements 105 4.1 Recursive Specification of Obs 105 4.2 Iteration Statements 111 4.3 Jump Statements 116 Procedures 123 5.1 F-procedures 123 5.2 Call-by-name, Side-effects 126 5.3 Integer Parameters 128 5.4 Label Parameters 145 Conclusion 147 Chapter 5. Chapter 6. References 150 vi CHAPTER 1 INTRODUCTION 1.1 Preliminary Remarks Before we can model programming languages, we have to be definite about what is to be regarded as the meaning of a program. In our view, the meaning of a program is a function. A program prescribes the computational steps which produce the value of some function corresponding to a given value of the function argument. It is precisely the function intended to be computed by a program that we take to be the meaning of the program. Consequently, in order to model programming languages mathematically, we seek to formulate rules for deriving the mathematical definitions of functions from the computational representations of functions provided in the form of programs. The problem of obtaining the mathematical definition of a function from the text of a program computing that function is quite non-trivial. To describe computations, programming languages make use of a number of concepts that are not present in the customary mathematical notations for representing functions. Central to the present-day programming languages -- and the main source of difference between the diction of mathematics and that of programming languages -- is the notion of a computer memory. program variable. Consider, for example, the concept of Whereas a mathematical variable denotes a 1 2 value, a program variable denotes an address in the computer memory. Or, compare the notion of substitution used in the functional calculi with the notion of assignment used in programming languages. Again, whereas the former is concerned with values, the latter is concerned with addresses. Similarly, the notion of function used in mathematics is radically different from the notion of procedure used in programming languages; the evaluation of functions requires straightforward substitution of the argument values in the functional definitions, while the execution of procedures requires rather elaborate manipulation of information involving a complex of memory locations. For a long time now, two types of constituents have been distinguished in programming languages [17] -- the descriptive elements, such as expressions and functions, and the imperative elements, such as assignments, instruction sequencing, and jumps. In addition, high-level programming languages also contain declarative elements, such as type, array, and block declarations, and name and value specifications for procedure parameters. Although the descriptive and the declarative elements are often lumped together [17,36], we feel that these two classes should be recognized as quite distinct; while the purpose of the former is mainly to designate values, the purpose of the latter is to remove ambiguities and to impose structure on program and data. Indeed, the declarative features often serve to interconnect the descriptive and the 3 imperative components of a program. The imperative constituents have their origin in machine languages from which the present-day high-level programming languages have evolved. The descriptive constituents have been introduced for ease and conciseness of notation, as well as for making the programs resemble more closely the functions they compute. The addition of such constituents to programming languages thus represents a step away from the machine and towards mathematics. The declarative constituents have been added for, ostensibly, improving the clarity and transparency of programs. But in actual fact, by introducing sophisticated address-related concepts, most declarative features represent a step back to the machine, and their presence often makes the recognition and extraction of the functional meaning of a program more difficult than would be in their absence. The descriptive features of programming languages lend themselves to mathematical interpretation in quite a natural manner. It is the presence of imperative and declarative features that obscures the functional meaning of a program. And the essence of that obscurity is in the dependence of these features upon the concept of computer memory. Thus, the key to the extraction of functional meaning of programs lies in modelling programming constructs without using the idea of memory address. This is the task that we undertake to do in the present dissertation. In particular, we seek to explain program variables in terms of mathematical variables, 4 the operation of assignment in terms of substitution, and procedures, programs, and, in general, all program statements in terms of functions. To express programming constructs, we make use of the notation and terminology of ALGOL 60 [27], with a few, explicitly stated, extensions. As the mathematical theory for modelling the programming constructs, we make use of the combinatory logic [8,9,33,34], originated by Schönfinkel and developed principally by Curry. A remarkable feature of this theory is the absence of variables. Nevertheless, functions can be represented by the objects of this theory in a natural manner. Thus, by using the combinatory interpretation of programming languages, we seek to eliminate variables altogether, program-related or mathematical. 5 1.2 Background A number of combinatory logic (or, related, lambda- calculus) models of programming languages have appeared in the literature. The most well known of these are due to Landin. In [16], Landin uses the lambda-calculus to model the semantics of expression evaluation in programming languages. He also prescribes the semantics of the lambda-calculus itself by means of an interpreter, the SECD machine, to evaluate lambdaexpressions. But in extending his model to include the imperative features of ALGOL 60, he elects [17] to supplement the lambda-calculus and its evaluator with such concepts as the assigner, the jump operator, and sharing. Due to their pioneering nature and their thoroughness of analysis, Landin’s papers have greatly influenced the subsequent work on programming semantics. But they have also been instrumental in creating the rather unfair impression that the pure lambdacalculus is not an appropriate medium for representing the imperative notions of programming languages (see for example [2]). The direct superimposition of imperative concepts on the lambda-calculus seems to us unsuitable on several grounds. First of all, there is no guarantee of the preservation of consistency in the augmented calculus. Then, by defining the new calculus in terms of the sequentially operating “sharing machine”, one throws away the most important property of the lambda-calculus, the Church-Rosser property [9], which implies 6 that the values of lambda-expressions are independent of their evaluators. Finally, the resulting model of programming languages forces one to identify a program with its execution trace; it fails in capturing the abstract function underlying the computation expressed in the program. Stratchey [36] also uses the lambda-calculus to model programming language semantics. Unlike Landin, he under-takes to represent the imperative as well as the declarative and descriptive notions of programming languages in terms of the descriptive concepts of the lambda-calculus. To account for the notion of assignment, however, he postulates a number of primitives to represent the generalized concepts of address and the related memory fetch and store operations. Thus, by not going as far as to eliminate program variables in favor of mathematical variables, Stratchey’s model is again computational rather than functional. As Burstall points out in describing another lambda-calculus model [5] of programming, it is unnecessary to introduce any assignment-related concepts as primitives. Indeed, Burstall shows how assignments can be naturally modelled by the lambda-calculus operation of substitution. We remark that our approach is closest to Burstall’s in spirit, though we adopt a different method of representing assignments. Orgass and Fitch [28] have developed a theory of programming languages in a system of the combinatory logic. They represent the computer memory as an n-tuple and the 7 machine state transitions caused by the execution of instructions as functions on n-tuples. Their representation of programming languages is in rather general terms, and it is not clear how it can be used to obtain the representation of specific programs. The main drawback in their model seems to be the lack of treatment of declarative features, so that their discussion of programming languages is applicable, for the most part, to machine-level languages only. A number of researchers have used the combinatory logic or the lambda-calculus to model only some important programming constructs rather than full-fledged programming languages. In [26], Morris explores the concepts of recursion and types in the lambda-calculus, but his work is also relevant to these concepts as they occur in programs. Ledgard [18] describes a model of type checking in which lambda-expressions are used to abstract out the type relations within a program. In addition, we mention the work of Böhm [4], Petznick [29], Milner [25] and Henderson [11], who obtain the representations of various elementary programming constructs and schematic programs in the combinatory logic or the lambda-calculus. The semantics of the lambda-calculus itself has attracted considerable attention. We have already mentioned Morris’ results on recursion and types [26] and Landin’s SECD machine [16] for evaluating lambda-expressions. The formal specification of several lambda-calculus interpreters (originally due to Wegner [37]), and proofs of their mutual 8 equivalence have been given by McGowan [24]. The Wegner- McGowan and Landin machines employ the so-called “call-byvalue” strategy of evaluating the right component of an application before the left one. Consequently, none of these machines is a true normal-form reducer for lambda-expressions. A computer to reduce the objects of the combinatory calculus to their normal forms has been designed and proved correct by Petznick [29]. This computer consists of stack-structured control registers and tree-structured memory, permits shared memory representations of equiform objects, and has provisions for incremental programming and multiple processing. Reynolds [31] defines and interrelates a number of interpreters for the lambda-calculus and for its various extensions with programming language features. Knuth [15] approaches the semantics of the lambdacalculus from the viewpoint of his general theory of the semantics of context-free languages, in which the meanings of sentences are built up, in the course of sentence generation, from the attributes associated with non-terminal symbols. Finally, the most abstract and incisive work on the semantics of the lambda-calculus has been done by Scott (e.g., [35]) as part of a very general theory of computation. Concerned with an abstract or functional explanation rather than with a computational explanation, Scott represents a lambdaexpression as the limit of a certain sequence of constructions on complete lattices. His theory succeeds in providing very 9 convincing answers to the problems related to recursion and self-application in the lambda-calculus. Combinatory models constitute but a minute fraction of the total work that has been done so far on the semantics of programming languages. models. We mention just a few of the other In [10], Floyd suggests the method of semantic definition of programming language statements by means of pairs of conditions that hold just before and after the statement execution. Apparently, little use has been made of Floyd’s method in the definition of programming languages. But his idea of representing the effect of execution of program statements by assertions has had diverse and far-reaching consequences. (For example, it serves as the basis of most program proving schemes [20]). Of all the methods of defining programming languages, the most elaborate and the most extensively applied is the “Vienna Method” [19,21] -- so called because it was developed at the IBM Vienna Laboratory for the semantic specification of the PL/l language. In this method, programming constructs are represented in terms of the (nondeterministic) state transitions of a machine; it thus constitutes a computational, rather than a functional, approach to semantics. Among the purely functional semantic models of programming languages based on theories other than the combinatory logic, the most notable are: Burstall’s [6] representation of programs by combining the Floyd-type assertions associated with the 10 programs into the formulas of first-order predicate calculus; and Manna and Vuillemin’s [22] Scott-theoretical interpretation of programming constructs as minimal fixed points of “recursive programs”. To conclude, we mention the axiomatic approach to programming semantics, which is different from both functional and computational approaches. It consists in devising the systems of axioms and inference rules which apply directly to programming language constructs, and in which the properties of programs may be derivable as theorems. This approach obviates the circuitousness involved in deriving the same properties when one uses a “programming model” (in which case, one first represents programs as the objects of some mathematical theory, and then works with these representations to derive the properties). Igarashi [13] and deBakker [2] are the first to propose sets of axioms dealing with elementary programming constructs; their systems are, however, rather complex. A very simple and elegant axiom system (in which Floyd’s ideas again find a new expression) has recently been presented by Hoare [12]. CHAPTER 2 THE COMBINATORY LOGIC This chapter summarizes the properties of the combinatory logic [8,9,33,34] which will be utilized later in the modelling of programming languages. The discussion is in terms of a particular system SK consisting of only two primitive objects (S and K), a primitive operation (combination) to form new objects from given ones, and two primitive relations (S- and Kcontractions) between objects. 2.1 Morphology and Transformation Rules The alphabet for SK consists of the symbols “S”, “K”, “(”, and “)”. As there is no possibility of confusion, we let “S” and “K” also denote the words consisting solely of S and K, respectively. If a and b denote words over the alphabet, then we denote by “(ab)” the word obtained by concatenating the symbol “(”, the word a, the word b, and the symbol “)”. Of all the words over the SK alphabet, we distinguish certain words by means of the (1-1) Definition. The (combinatory) obs are formed according to the following rules: (1) S and K are obs. (2) If a and b are obs, then (ab) is an ob. 11 12 (3) The only obs are those specified by (1) and (2). S and K are primitive obs; any other ob, which is necessarily of the form (ab), is a composite ob. The composite ob (ab) is the application of a to b, or the combination of a and b, the obs a and b being its left and right immediate components, respectively. It follows that the immediate components of an ob are uniquely determined and are nonoverlapping. A component of an ob is either the ob itself or a component of an immediate component of the ob. A proper component of an ob is a component which is not the ob itself. The length of an ob is the number of symbols Example. S and K in it. The application of the ob ((KS)((SS)K)) to K is the composite ob (((KS)((SS)K))K) of length 6, some of whose proper components are K, S, (KS), and ((SS)K). The same ob S has three different occurrences as components of the above ob; all these occurrences are to be regarded as different components. We use the notation a ≡ b to indicate that the obs a and b are equiform, that is, spelt the same over the SK alphabet. We also use this notation for introducing a as a new name for the ob b. We may abbreviate obs by omitting parentheses under the convention that any omitted parentheses are to be re-inserted by association to the left. For example, ((SS)(SK)) maybe abbreviated to (SS)(SK) or SS (SK). We now state a number of rules for transforming obs. 13 (1-2) Definition. (1) For all obs a, b, and c: The ob Sabc S-contracts to the ob ac(bc); in symbols, Sabc →S ac(bc). (2) The ob Kab K-contracts to the ob a; in symbols, Kab →K a. If a →S b, then a and b are called, respectively, the S-redex and S-contractum corresponding to each other. K-contractum are defined analogously. K-redex and A redex is either an S-redex or a K-redex. Let an ob b be obtained from an ob a by replacing a component c of a by an ob d. If it is the case that c →S d or c →K d, then a immediately reduces to b (in symbols, a →im b). For example, K(SKSa)Sb →im SKSab, and also K(SKSa)Sb →im K(Ka(Sa))Sb, depending on the redex selected for contraction. (1-3) Definition. An ob a is irreducible or in normal form if there is no ob b such that a →im b. Thus, no component of an irreducible ob may be a redex. Some examples of irreducible obs are S, K, KS, SKK, and SS(S(SSK)K). (1-4) Definition. An ob a reduces to an ob b, denoted a → b, if there exist obs a0, a1,..., an, for some n > 0, such that (1) a ≡ a0 , (2) b ≡ an , 14 (3) Example. ai →im ai+1 , for 0 ≤ i < n . SKSKSKabc → ac(bc), since SKSKSKabc →im KK(SK)SKabc →im KSKabc →im Sabc →im ac(bc). If a →S b, then we also say that b S-expands to a, and write b ←S a. K-expansion In a similar manner, we define (←K), immediate expansion (←im), and expansion (←). (1-5) Definition. An ob a is interconvertible with an ob b, denoted a ↔ b, if there exist obs a0,a1,...,an, for some n > 0, such that (1) a ≡ a0 , (2) b ≡ an , (3) ai →im ai+1 or ai ←im ai+1 , for 0 ≤ i < n. Example. SKKa ↔ SSSSKa, since SKKa →im Ka(Ka) →im a ←im Ka(SSKa) ←im SK(SSK)a ←im SS(SS)Ka ←im SSSSKa. Reduction (→) and interconvertibility (↔) are related by the well-known (1-7) Theorem (Church-Rosser) [9]. If a ↔ b, then there exists an ob c such that a → c and b → c. (1-8) Corollary. If the obs a and b are irreducible, then a ↔ b if and only if a ≡ b. (1-9) Definition. A normal form of an ob a is an irreducible ob b, if one exists, such that a ↔ b. An ob is normal 15 if it has a normal form. From Theorem 1-7 and Corollary 1-8, it immediately follows that: (1-10) Theorem. unique. If an ob is normal, then its normal form is Moreover, if b is the normal form of a, then a → b. That is, it is possible to discover the normal form of a normal ob by a sequence of contractions alone, starting from the given ob. Of course, there are obs that are not normal. An example of such an ob is (aaa), where a ≡ SSK. It is easily seen that all possible reductions of the ob (aaa) either keep producing longer and longer obs or eventually lead to (aaa) again, so that they will continue indefinitely without ever reaching an irreducible form. Moreover, there are obs that are normal but for which not every reduction terminates in a normal form. This is illustrated by the ob KS(aaa), with a defined as above; its normal form S cannot be reached as long as reductions are carried out only inside the component (aaa). Fortunately, there exists a deterministic reduction procedure such that, when applied to normal obs, it always arrives at their normal forms in a finite number of steps. It is as follows: (1-11) Standard Order Reduction Algorithm (Church-Rosser). [9) To obtain the normal form of a normal ob, start with the ob and successively apply contractions of the leftmost redex, until no further reduction is possible. A modification of the above scheme is used in Petznick’s 16 combinatory computer [29], a hypothetical machine for reducing obs to normal form. Obs may be so represented in the memory of this machine that all equiform components of an ob may have but one internal representation. The reduction takes place by contracting the leftmost redex; but as a consequence of shared representations, several equiform redexes may be contracted simultaneously. On account of Definition 1-9 and Theorem 1-10, it seems reasonable to regard the normal form of an ob as the “value” of the ob, and the process of normal form reduction as “evaluation”. Consequently, we may regard mutually interconvertible normal obs as “equal” since they have the same value. This view of value and equality is similar to the one taken in other calculi. For example, among the equal arithmetic expressions 2x2+3x4, 4+12, and 16, the last one is distinguished in being “irreducible” by the rules of arithmetic, and thus it is regarded as the value of the three expressions. For obs as well as for arithmetic expressions, it so happens that the value is obtainable by a sequence of reductions only, and it is unique despite the possible nondeterminism involved in the order of reductions. But unlike the case for arithmetical expressions, there is no algorithm to decide whether or not two given obs have the same value (i.e., are interconvertible). The notion of interconvertibility is generalized in the 17 following (1-12) Definition. An ob a is n-interconvertible to an ob b, in symbols a ↔n b if for all obs c1,...,cn , acl...cn ↔ bc1...cn Clearly, a ↔n b implies a ↔m b for all m > n; but the converse does not hold. For example, we have SKS ↔1 SKK, since SKSc → c ← SKKC for all c. Yet SKS ¬↔0 SKK (i.e., SKS ¬↔ SKK) by Corollary 1-8, as the two obs are irreducible but not equiform. Indeed, it can easily be shown that for a given normal ob a and an integer n ≥ 1, there exist infinitely many obs b with distinct normal forms such that a ↔n b but, for all m < n, a ¬↔m b. 18 2.2 Functional Abstraction Our interest in SK derives from the fact that we can represent programming language constructs (e.g., expressions, statements, programs) by obs, and the processes required in the execution of programs (e.g., substitution, expression evaluation, procedure application) by the reduction operation. Such a representation is possible because 1. Programming constructs can be regarded intuitively as functions (in a special sense of the word, explained below), and 2. Functions can be represented by obs. This section will describe how to represent functions as obs. We will assume that a function F is defined by means of a functional equation of the form F(x1,...,xn) = E in which E is an expression that may contain constants, variables, and already defined functions. The variables x1,...,xn are called the formal arguments of the function F. To obtain the value of this function for a list of expressions given as actual arguments, the actual arguments are substituted in place of the corresponding formal arguments, and the resulting expression is evaluated. Confined for the moment to the functions of one argument only, our basic approach to the SK representation of functions may be stated as follows: Let F be a given set of 19 one-argument functions to be represented, and let A be the union of the domains and ranges of the given functions. Then we choose obs to represent the members of F and A so as to satisfy the following condition. For all F in F and a,b in A, and their respective SK representations F, a, b, if F(a) = b, then (F a) → b. The above representation applies as such to one-argument functions only. But a basic idea of the combinatory calculus, due to Schönfinkel [34], is to regard even the functions of several arguments as just one-argument functions. This becomes possible if the domains and ranges of one-argument functions are permitted to contain one-argument functions themselves. To see how this idea works, suppose F is a function of two arguments, and let Gx (for a given x) and H be one-argument functions such that Gx(y) = F(x,y) , H(x) . = Gx Then for any arguments a and b, we have F(a,b) = Ga(b) = [H(a)](b) Now F is identified with the one-argument function H, and, consequently, F(a,b) with [H(a)](b). Hence, designating the representative obs by underlining the names of the represented functions or constants, we may choose F = H, thereby 20 representing F(a,b) by ((H a)b), i.e., (F a b). (F a) represents H(a), i.e. Note that the function Ga . In general, if F is a function of n arguments, then: (F a1...am) for m < n represents the function G such that G(xm+1,...,xn) = F(a1,...,am,xm+1,...,xn) , and (F a1...an) represents F(a1,...,an). With the above interpretation of functions in mind, every expression involving only functions and constants but not variables, written in the customary functional notation using function application and composition, is representable in SK, provided that its constituent functions and constants are representable. For example, we may represent the expression F(G(a,b),H(J(c),d)) by the ob F(G a b)(H(J c)d), and carry out the evaluation of the former entirely within SK by reducing the latter. But in order to extend our representation to the expressions involving variables also, we require a generalization of obs described next. We adjoin a denumerable collection of symbols called indeterminates to the alphabet of SK. We do not specify these symbols; but it will always be possible to infer from the context whether or not a symbol is an indeterminate. Of all the words over the augmented SK alphabet, we characterize an ob form to be a word that either consists of a single indeterminate or S or K, or is of the form (e1 e2), 21 where e1 and e2 are ob forms (Cf. Definition 1-1). All the definitions, notational conventions, and properties related to obs, as given in the previous section, generalize in an obvious manner for ob forms. Given an ob form e and an indeterminate x, we say that e contains x, or x occurs in e, in symbols, x oc e, if x is a component of e; x oc / (2-1) Definition. e if it is not the case that x oc e. Let e,f1,...,fn be ob forms and x1,...,xn be distinct indeterminates. Then the result of (simultaneous) substitution of f1 for x1, f2 for x2 ,..., fn for xn in e, denoted sub [f1,x1;...;fn,xn;e] is defined by induction on n and the structure of e as follows: (1) sub [f1,x1;e] e , if x1 oc / ≡ e, f1 , if e ≡ x1 (sub [f1,x1;g]sub [f1,x1;h]), otherwise, where e ≡ (gh). (2) sub [f1,x1;f2,x2;...;fn+1,xn+1;e] ≡ sub [f1,z; sub [f2,x2;...;fn+1,xn+1;sub [z,x1;e]]] where z is an indeterminate which is distinct from each of x1,...xn and which does not occur in any of e, f1, ..., fn . 22 As a consequence of the definitions of reduction and substitution, we have (2-2) Lemma. If e → e’, then sub [f1,x1;...;fn,xn;e] → sub [f1,x1;...;fn,xn;e’]. (2-3) Definition. Given an ob form e and indeterminates x1,...,xn, for some n ≥ 1, an abstract of e with respect to x1,...,xn is an ob form f such that (1) xi oc / f , l ≤ i ≤ n , (2) fx1...xn → e. Example. For e ≡ xyz(y(xy)z), we have e ← S(xy)(y(xy))z ← SSy(xy)z ← S(SS)xyz . Hence, the ob S(SS) is an abstract of e with respect to x,y,z; the ob forms S(SS)xy, SSy(xy), and S(xy)(y(xy)) are all abstracts of e with respect to z. Let f be an abstract of an ob form e with respect to the indeterminates x1,...,xn. The relations (1) and (2) of the above definition are satisfied. Applying Lemma 2-2 to (2), we have for all ob forms g1,...,gn , sub [g1,x1;...;gn,xn;fx1...xn] → sub [g1,x1;...;gn,xn;e]. Because of (1), we can simplify the left-hand side in the above relation and restate the relation as follows: If f is an abstract of e with respect to x1,...,xn, then for 23 all ob forms g1,...,gn, we have f g1...gn → sub [g1,x1;...;gn,xn;e]. (*) Further properties of abstracts will be described later. Earlier in this section, we have noted how the expressions that contain already represented constants and functions can be represented by obs. With variables represented by indeterminates (possibly, with the same symbolic denotation), we can extend that scheme to represent by ob forms the expressions that contain variables in addition to constants and functions. Now consider a function F defined by the functional equation F(x1,...,xn) = E , in which E is an expression containing constants, variables, and already defined functions. The value of F for given actual arguments G1,...,Gn is computed by simultaneously replacing x1 with G1, ..., xn with Gn in E, and then evaluating the resulting expression. Let e, g1,...,gn be the ob forms representing the expressions E, G1,...,Gn, respectively. Then we would like to represent the function F by an ob form (or, if possible, by an 1 ob ) f satisfying 1 It will soon become clear that if, in the functional equation F(x1,...,xn) = E defining F, the expression E does not involve any variable other than x1,...,xn, then F is representable by an ob. 24 the relation fg1...gn → sub [g1,x1;...;gn,xn;e] Furthermore, the above relation must hold for all choices of ob forms g1,...,gn. But we have just seen that this is precisely the case if f is an abstract of e with respect to x1,...,xn. Hence, given a functional equation defining a function, any abstract of the ob form representing the right-hand expression in the equation with respect to the formal arguments can be taken as the SK representation of that function. From the property (*) of abstracts obtained earlier, it follows that all abstracts of the same ob form with respect to the same n indeterminates are mutually n-interconvertible (Definition 1-12). Also, an ob form which is n- interconvertible to an abstract of a given ob form with respect to n given indeterminates is itself one such abstract. To designate an arbitrarily chosen abstract of the ob form e with respect to the indeterminates x1,...,xn , we employ the a notation (A x1...xn:e). This notation may be abbreviated by omitting parentheses under the convention that the ob form to the right of the colon sign extends as far to the right as is consistent with its being well formed. a a abbreviate (A xy:(A z:(x(xy)z)) to a For instance, we may a A xy:A z:x(xy)z. We state below some important properties of abstracts, including (*) for completeness; some simple arguments pertaining to the 25 substitution process suffice to establish these properties: (2-4) Theorem. Let e, e’, g1,...,gn be ob forms and x1,...,xn distinct indeterminates for some integer n ≥ 1. Then: a (1) (A x1...xn:e) g1...gn → sub [g1,x1;...;gn,xn;e]. (2) If y1,...,yn are distinct indeterminates and yi oc / e for 1 ≤ i ≤ n, then a a A x1...xn:e ↔n A y1...yn:sub [y1,x1;...;yn,xn;e]. (3) a) If x oc / e for all 1 ≤ i ≤ n, then a A x1...xn:ex1...xn ↔n e. b) Generally, for an integer m ≤ n, if xi oc / e for m ≤ i ≤ n, then a a A x1...xn:exm...xm ↔n A xl...xm-l:e. (We consider the right-hand side to be simply e when m = 1.) a a a (4) A x1...xi-1:A xi...xn:e ↔n A x1...xn:e, 1 ≤ i ≤ n. (5) If e ↔ e’, then A x1...xn:e ↔n A x1...xn:e’. a a Since a function of n arguments can be represented equally well by any one of a certain class of n-interconvertible obs, the above theorem suggests several ways of choosing simple functional representations. For example, to represent the function F given by F(x1,...,xn)= E, one can take a the ob A x1...xn:e’, where e’ is the normal form 26 of the ob form e representing the expression E. The properties given in Theorem 2-4 hold for all abstracts. We may expect that by choosing abstracts in some specific manner, these properties could be strengthened, thus simplifying our work with the abstracts. That this is indeed the case is shown by the following description of (a modified version of) Rosser’s abstraction algorithm [33] and the improved properties of the associated abstracts. (2-5) Definition. indeterminates. Let e be an ob form and x1,...,xn be The R-abstract of e with respect to x1,...,xn, R denoted (A x1...xn:e), is defined by induction on n and the structure of e as follows: R (1) (A x1:e) Ke , if x1 oc / e, SKK, if e ≡ x1, ≡ f , if e ≡ (fx1) and x1 oc / f, R R S(A x1:f)(A x1:g), otherwise, where e ≡ (fg) . R R R (2) (A x1x2...xn+1:e) ≡ (A x1: (A x2...xn+1:e)). Again we may omit parentheses in writing R-abstracts with the understanding that the ob form to the right of the colon extends as far to the right as its well-formedness would permit. 27 Example. R R R A z:xz(yz) ≡ S(A z:xz)(A z:yz) ≡ Sxy, R R R R A yz:xz(yz) ≡ A y:A z:xz(yz) ≡ A y:Sxy ≡ Sx, R R R R A xyz:xz(yz) ≡ A x:A yz:xz(yz) ≡ A x:Sx ≡ S, R R R A x:xy ≡ S(A x:x)(A x:y) ≡ S(SKK)(Ky) . We obviously have x oc / R A x:e, and, in general, for all R 1 ≤ i ≤ n, xi oc / A x1...xn:e . indeterminates y, if y oc / Note also that for all e, then y oc / R A x1...xn:e. Thus, if the ob form e contains no indeterminates other than xl,...,xn, R then A x1...xn:e is just an ob. (2-6) Lemma. If, for all 1 ≤ i ≤ n, xi oc / f and y ¬≡ xi, then R R sub [f,y;A x1...xn:e] ≡ A x1...xn:sub [f,y;e] . Proof. Repeated application of Curry’s Corollary 4.1 [9, p. 208]. (2-7) Theorem. Let e, e’, g1,...,gn be ob forms and xl,...,xn distinct indeterminates for some integer n ≥ 1. Then: R (1) (A x1...xn:e)g1...gn → sub [g1,x1;...;gn,xn;e]. (2) If y1,...,yn are distinct indeterminates and yi oc / for 1 ≤ i ≤ n, then R R A x1...xn:e ≡ A y1...yn: sub [y1,x1;...;yn,xn;e]. (3) a) If xi oc / e for all 1 ≤ i ≤ n, then R A x1...xn:ex1...xn ≡ e. b) Generally, for an integer m ≤ n, if xi oc / e e 28 for m ≤ i ≤ n, then R R A x1...xn:exm...xn ≡ A xl...xm-l:e. (The right-hand side is considered to be simply e when m = 1.) R R R (4) A x1...xi-1:A xi...xn:e ≡ A x1...xn:e, 1 ≤ i ≤ n. (5) If e ↔ e’, then R R A x1...xn:e ↔n A x1...xn:e’ . Proof. For the case n = 1, parts (1) and (2) can be verified by induction on the structure of e, and part (3) is true by the definition of A can be proved by induction. . For n > 1, they Assuming (1) true for n ≤ k, we show it for n = k+l, as follows. such that z oc / R Choose an indeterminate z e and, for 1 ≤ i ≤ k+l, z ¬≡ xi and z oc / gi . Then: R (A x1x2...xk+1:e)g1g2...gk+l R R ≡ (A x1: (A x2...xk+1:e)) g1g2...gk+l R → sub [g1,x1;( A x2...xk+1:e)] g2...gk+l , by the case n = 1, R ≡ sub [g1,z;sub [z,x1;( A x2...xk+1:e)]] g2...gk+l R ≡ sub [g1,z;( A x2...xk+1:sub [z,x1;e])] g2...gk+l , by Lemma 2-6, 29 R ≡ sub [g1,z;( A x2...xk+1:sub [z,x1;e])] sub [g1,z;g2]...sub [g1,z;gk+1] by Definition 2-1 (1), since z oc / g2,...,gk+1, R ≡ sub [g1,z;(( A x2...xk+1: sub [z,x1;e]) g2,...,gk+1)] → sub [g1,z; sub [g2,x2;...;gk+1,xk+1; sub [z,x1;e]]] , by the induction hypothesis and Lemma 2-2, ≡ sub [g1,x1;g2,x2;...;gk+1,xk+1;e] , by Definition 2-1 (2) . Proofs of other parts are quite simple and are omitted. It has already been mentioned that xi oc / for all 1 ≤ i ≤ n. R A x1...xn : e To verify that R-abstracts are indeed abstracts (Definition 2-3), it suffices to replace gi by xi, 1 ≤ i ≤ n, in Part (1) of Theorem 2-7. Thus, now we have an algorithm for the SK representation of functions that are defined by functional equations. A comparison of Theorems 2-4 and 2-7 shows that R-abstracts possess simpler properties than the abstracts in general. One exception is the property in Part (5) of Theorem 2-7, where we do not get any improvement by using R-abstracts. The relation ↔n in the conclusion of that part cannot be strengthened to ↔ , as the following counterexample will R R show: Kx(Sx) ↔ x, A x:Kx(Sx) ≡ SKS, A x:x ≡ SKK, and SKS ↔1 SKK, but not SKS ↔ SKK (cf. discussion after Definition 1-12). 30 In spite of their simpler properties as compared to abstracts in general, R-abstracts rapidly increase in length and become tedious to compute as the number of indeterminates for abstraction increases. In Section 2.4, we will describe an alternative, more practical algorithm for obtaining abstracts that have almost the same reduction properties as R-abstracts. 31 2.3 The Lambda-Calculus The previous section has introduced the notions of indeterminates, ob forms, and abstracts for the purpose of representing functions in SK. It so turns out that the functions encountered in the representation of programs are defined solely in terms of formal variables; the resulting SK representations are, therefore, just obs, rather than ob forms containing indeterminates. Thus, eventually, the use of indeterminates and the generalization of obs to ob forms are both dispensable; they are employed as a matter of notational convenience only. An alternative notion is that of lambda-expressions, which closely resemble abstracts in their properties, but in which the use of variables (indeterminates) is not as incidental. Lambda-expressions are the objects of the lambda-calculus LC , which may be regarded either as an augmentation to SK or an independent formal system that is similar to SK in several respects. We present below a brief description of lambdaexpressions for the sake of completeness and comparison with abstracts. For details, consult [8,9,33]. The symbols in the alphabet of LC are “(”, “)”, “λ” , “:”, and a denumerable collection of variables. A lambda-expression (LE) is either a variable, or a word of the form (ef) or (λx:e), where e and f are LE’s and x is a variable. The LE (ef) is the application of e to f, and the LE 32 (λx:e) is the abstraction of e with respect to x. To abbreviate LE’s, we may omit parentheses under the convention that applications associate to the left and abstractions to the right, with the former taking precedence over the latter. For instance, the LE (λx: (λy: ((xy) (λz: (((xz) (yz)) u))))) may be abbreviated to λx: λy: xy(λz: xz(yz)u) . As an additional convention, the above may be further abbreviated to λxy: xy(λz: xz(yz)u) In the LE (λx:e), the leftmost occurrence of x is a binding occurrence, and e is the range of that occurrence. An occurrence of a variable in an LE is bound if it is either binding or in the range of any binding occurrence of the same variable; otherwise, the occurrence is free. If e,f1,...,fn are LE’s and x1,...,xn variables, then sub [f1,x1;...;fn,xn;e] denotes the result of simultaneously substituting fi for all free occurrences of xi (1 ≤ i ≤ n) in e. The basic LC rules for transformation, called contractions, are these: 33 (a) λx:e →α λy:sub [y,x;e] , provided that y has no free occurrences in e, and no free occurrence of x in e becomes a bound occurrence of y by the substitution. (b) (λx:e)f →β sub [f,x;e] , provided that no variable with free occurrences in f has bound occurrences in e. (c) λx:ex →η e, provided that x has no free occurrences in e. Analogously to the development in the case of SK, we define: immediate reduction (→im) of LE’s as the application of an α-, β-, or η-contraction on their parts; 2 reduction (→) as a sequence of immediate reductions; expansion (or, respectively, α-, β-, η-, immediate expansion) as the converse of reduction (or, respectively, α-, β-, η-contraction, immediate reduction); and interconvertibility (↔) as a possibly empty sequence of immediate reductions and expansions. (The use of the same symbols → , →im and ↔ to represent the different relations of SK and LC should not cause any confusion.) The Church-Rosser theorem holds for LC as well [9, Ch. 4]: If e ↔ f, then there exists an LE g, such that e → g and f → g. 2 But note the differences from SK: The LE e is an immediate part of the LE’s (e f), (f e), and (λx:e), where f is an LE and x is a variable. A part of an LE is either the LE itself or a part of an immediate part of the LE. 34 An LE is irreducible if no β- or η-contraction is applicable to it even after any applications of α-contraction. Given an LE e, if there exists an irreducible LE f such that e ↔ f, then f is a normal form of e. The normal forms of LE’s are unique only up to the applications of α-contraction. The following properties of LE’s can be derived easily from the above definitions. (3-1) Theorem. (1) If none of y1,...,yn has a free occurrence in e and no free occurrence of xi, 1 ≤ i ≤ n, in e becomes a bound occurrence of the corresponding yi in e by the substitution below, then (2) λx1...xn:e → λy1...yn:sub [y1,x1;...;yn,xn;e]. If no variable with a free occurrence in any of g1,...,gn has a bound occurrence in e, then (λx1...xn:e)g1...gn → sub [g1,x1;...;gn,xn;e]. (3) If none of x1,...,xn has a free occurrence in e, then λx1...xn:ex1...xn → e. (4a) If e → e’, then λx1...xn:e → λx1...xn:e’. (4b) If e ↔ e’, then λx1...xn:e ↔ λx1...xn:e’. The similarity between LC and SK (augmented with ob forms) becomes obvious when we set up a correspondence between variables and indeterminates and then interpret (1) Ob forms by LE’s, by replacing S and K with λxyz:xz(yz) and λxy:x, respectively; (2) R LE’s by ob forms, by replacing λ with A . 35 With this interpretation, it is easy to see that whenever two ob forms are mutually related by the SK reduction, their corresponding LE’s are related by the LC reduction. converse, however, is not true. The Here is a counterexample. Let f ≡ λx: (λxy:x)x((λxyz: xz(yz))x) → λx: sub [x,x; (λxyz:xz(yz))x,y; x] ≡ λx:x ≡ g , say. Let fC and gC denote the SK interpretations of the LE’s f and g, respectively; that is, R R R fC ≡ A x: (A xy:x)x ((A xyz: xz(yz))x) , R gC ≡ A x:x ≡ SKK . It can be verified easily that fC ≡ SKS. Thus, in spite of the relation f → g , it is not the case that fC → gC . The above example also provides the explanation of this dissimilarity of behavior between LC and SK. During the process of reduction in LC, contractions can be applied to any part of an LE, including the one to be abstracted (i.e., to the right of the colon) in an abstraction. In SK an R-abstract is an abbreviation for an ob form, and there are no provisions for applying contractions on the part to be abstracted before the whole abstract is computed. (Compare Parts (4) of Theorems 2-7 and 3-1.) Thus, while all SK reductions can be carried out (in 36 terms of interpretations) in LC, additional reductions are possible in LC that have no SK counterparts. By supplementing SK with some special rules, its reductions may be made to correspond exactly with those in LC [9, p. 218]. The modified reductions are termed strong; in contrast, SK reductions are weak. But in strengthening the reductions, one loses the extreme simplicity of the SK reduction process as presently based on S- and K-contractions alone. Although weaker, the SK reductions completely suffice for the modelling of programming languages, and in the sequel, we describe our model in terms of SK only. But, since all of the reductions that we use also permit LC interpretations, the model may just as well be regarded as being in LC; the only needed modification is to replace the symbol A (the future R alternative to A ) by A, and to consider S and K as abbreviations for the LE’s respectively. λxyz:xz(yz) and λxy:x, A purely LC formulation of our model is given in [1]. We also remark that LC supports extensionality -- the property that if fa ↔ ga for all a, then f ↔ g -- while SK does not. As a result, two representations of the same intuitive function of n arguments are mutually interconvertible in LC but, in general, only n-interconvertible in SK. This, however, is no hindrance at all: It is the mechanical process 37 of functional evaluation that we keep simple by adhering to weak reductions. But in making formal arguments about functions, such as proving equivalence of functions and in choosing function representations, we may freely employ ninterconvertibility, which is as easy to use in these cases as interconvertibility. 38 2.4 Additional Obs In this section, we define a number of obs and ob families that will be employed in the representation of programs. We begin by introducing the most important of these: (4-1) Definition. I ≡ SKK , B ≡ S(KS)K , C ≡ S(S(KS)(S(KK)S))(KK) , W ≡ SS(SK) , Z ≡ KI , T ≡ CI , D ≡ WI , β ≡ B(BS)B, Y ≡ B(SWW)B , Ω ≡ YK . Clearly, I, B, C, and W are normal obs. Though it may not be so obvious from their definitions, the obs Z, T, D, β, and Y can be easily seen to be normal also. However, it will follow from the property of Y given below that Ω does not possess a normal form. We shall use the term “rule” to designate frequently used reduction properties. We list a set of rules (including S- and K-contraction for completeness) that can be easily derived from the above definitions. (4-2) Rule. For all obs a, b, c, d: (1) Sabc → ac(bc) , (2) Kab → a , (3) Ia → a , 39 (4) Babc → a(bc) , (5) Cabc → acb , (6) Wab → abb , (7) Zab → b , (8) Tab → ba , (9) Da → aa , (10) βabcd → a(bd)(cd), (11) Ya → a(Ya) , (12) Ωa → Ω . We may wish to incorporate some rules directly in an ob reducing mechanism in order to avoid intermediate reduction steps. This places the selected rules at par with contractions. Equivalently, we may wish to extend the calculus by admitting the obs with such rules as new primitive obs (in addition to S and K), and the rules themselves as contractions. In general, we have the choice of many rules for the same ob. As an example, for the ob B we have: (a) B → S(KS)K (b) Ba → S(Ka) (c) Bab → S(Ka)b (d) Babc → a(bc) (e) Babcd → a(bc)d For the sake of determinateness in reduction, however, we should allow only one rule for each primitive ob. if B is regarded as a primitive ob and (d) as the (Note that 40 “B-contraction,” then, by Definition 1-4, the ob Bab has to be considered irreducible whenever a and b are irreducible.) Although SK is formulated in terms of S and K alone, we shall state unique rules for the obs that are important enough to be candidates for use as primitives in an extended calculus. Now, whenever any extensions to SK are stipulated, the following question naturally arises: Do the Church-Rosser Theorem and related properties remain valid in the extended calculus? Fortunately, this theorem holds in very general situations involving replacement rules (Rosen [32]). It follows from Rosen’s work that the theorem is supported, for example, by the calculus containing S, K, and the obs of Definition 3-1 as primitives and Rules 3-2 as contractions. Furthermore, the standard-order reduction algorithm continues to be valid in the extended calculus. We shall next describe the representation of natural numbers and number-theoretic functions. Following Church [8, Chap. 3], we represent a natural number n by an ob n with this desired property: (4-3) nxy → x(x( ... (x y) ...)) . ↑ n occurrences of x The ob n is defined inductively in terms of the obs suc and 0, representing the successor function and zero, respectively, as follows: 41 (4-4) Definition. suc ≡ SB , 0 , ≡ Z n+1 ≡ suc n . That is, we have 0 ≡ Z , 1 ≡ SBZ , 2 ≡ SB(SBZ) , ... . Using the above definitions, we immediately obtain: (4-5) Rule. (1) 0xy → y , (2) n+l xy → x(nxy) , from which the property (4-3) follows by induction. At this point, we introduce some notation due to Curry [9]: We write a°b for Bab and a(n) for nBa. Consequently, we have: (4-6) Rule. (1) (a°b)c → a(bc), (2) a(n)bcl...cn → a(bc1...cn). In particular: (3) K(n)ab1...bnc → ab1...bn , (4) B(n)ab1...bncd → ab1...bn (cd). In expressions involving ° we shall regard ° as being of lower precedence than application and of higher precedence 42 than another ° at the right. Thus a°bc will stand for a°(bc), not (a°b)c, and a°b°c for (a°b)°c, not a°(b°c). Note, however, that a°b°c ↔1 a°(b°c). Anticipating the forthcoming discussion of tuples, we next introduce the representation of ordered pairs and triples. (4-7) Definition. <a,b> ≡ C(Ta)b , <a,b,c> ≡ C<a,b>c . The definition yields the following reduction properties. (4-8) Rule. (1) <a,b>c → cab , (2) <a,b>K → a (3) <a,b,c>d → dabc , (4) <a,b,c>(K°K) → a, and <a,b>Z → b , <a,b,c>(KK) → b, <a,b,c>(KZ) → c. We can now describe the representation of the predecessor function on natural numbers. (4-9) Definition. pred ≡ <S(BCT)suc°T0, <0,0>,K>. (4-10) Rule. 0, if n = 0, pred n → n-1, if n > 0. To prove this rule, let h ≡ S(BCT) suc°T0. easily shown that h <m, n> → <n, n+l> . Then it can be 43 Hence, pred n ≡ <h, <0,0>, K> n → n h<0,0>K → h(h(h( ... (h <0,0>) ... )))K ↑ n occurrences of h <0,0>K → 0, if n = 0 , <n-1,n>K → n-1, if n > 0 . → It will be found convenient to define the ob families by which the properties of K, I, and B are generalized in the following manner. (4-1l) Rule. (1) Knab1...bn → a, m (2) I nal...an → am , n ≥ 1 . n ≥ m ≥ l , m (3) B nab1...bmc1...cn → a(blcl...cn)...(bmcl...cn), m,n ≥ 1 . Thus, we will have K ↔2 K1 , 1 1 1 I ↔1 I 1 , B ↔3 B 1 , S ↔3 B 2I, a(n) ↔n+1 B1na . The above rules can be realized by making the following (4-12) Definition. (1) Kn ≡ n K , (2) n ≥ 1 , m I n ≡ m-1 K (n-m K), n ≥ m ≥ l , m (3) B n ≡ n(m-l(C(3 B) β ° TI)B) , m,n ≥ 1 . 44 It is easy to verify parts (1) and (2) of Rule 4-l1 from the above definitions. We shall prove part (3). Let h ≡ C(3 B) β ° TI. (*) We first show by induction on m that m-1 hBab1...bmc1 → a(b1c1)...(bmc1), (m ≥ 1). This result is immediate for m = 1. Assume it is true for m = k; then k h Bab1...bm+1c1 → h(k-l h B) ab1...bm+1c1 → (C(3 B) β ° TI) (k-1 h B) ab1...bm+1c1 → C(3 B) β (TI (k-1 h B)) ab1...bm+1c1 → 3 B(TI(k-1 h B)) β ab1...bm+1c1 → 3 B(k-1 h BI) β ab1...bm+1c1 → k-1 h B I(βa b1b2) b3...bm+1c1 , by 4-6 (2) , → I(βab1b2c1)(b3c1)...(bm+1c1), by induction hypothesis, → a(b1c1)(b2c1) ... (bm+1c1) . So (*) has been established. induction on n. To prove 4-1l (3) now, we use For m ≥ 1, we have m B 1 ab1...bmc1 ≡ 1(m-l hB) ab1...bmc1 → m-1 hB(0(m-lhB))a) b1...bmc1 → m-l hB ab1...bmc1 → a(b1c1)(b2c1) ... (bmc1) , by (*). 45 Hence assuming 4-11 (3) true for m ≥ 1 and n = k, we obtain m B k+1ab1...bmc1...ck+1 ≡ k+1(m-l h B) ab1...bmc1...ck+1 → m-l hB (k(m-l hB)a) b1...bmc1...ck+1 → k(m-l hB a(b1c1)...(bmc1)c2...ck+1 , by (*), m B k a(b1c1)...(bmc1)c2...ck+1 ≡ → a(b1c1c2...ck+1)... (bmc1c2...ck+1) , by induction hypothesis. As shown by the next definition and the succeeding rues, it is possible to generate the above ob families from single obs. We can thus avoid postulating infinitely many primitives in an extended calculus. (4-13) Definition. (1) K ≡ TK , (2) I ≡ βB(CpredK)(CCK°Tpred) , (3) B ≡ T°C(Cpred(C(3B) β°TI))B . (4-14) Rule. (1) K n → Kn , (2) m I m n → In , m (3) B m n → B n , Proof. Omitted. n ≥ l , n ≥ m ≥ l , m,n ≥ l . 46 Before listing further obs, we state an algorithm to m m obtain abstracts in terms of the obs Kn , I n , and B n . Unlike the algorithm of Definition 2-5 in which abstraction is carried out for one indeterminate at a time, the present algorithm performs the abstraction with respect to all specified indeterminates in a single step, and amounts to simple replacements of indeterminates with special obs. (4-15) Definition. (1) An initial component of an ob form is either the ob form itself or the left immediate component of an initial component of the form. (2) A primal component of an ob form is either its shortest initial component, or a right immediate component of one of its initial components. Example. The ob form e ≡ SK(x(KK)yz)(S(wz)(SSy))(xyz) has five initial components, namely, S, SK, SK(x(KK)yz), SK(x(KK)yz)(S(wz)(SSy)), and e itself; the primal components of e are S, K, x(KK)yz, S(wz)(SSy), and xyz. (4-16) Definition. Let e be an ob form and x1,...,xn (n ≥ 1), be indeterminates. Then the *-abstract of e with respect to x1,...,xn, denoted A*x1...xn:e, is the first of the following ob forms, selected in the given order, according as the condition is satisfied: 47 (1) Kne , if xi oc / (2) I (3) e for all 1 ≤ i ≤ n, , if e ≡ x1...xn, In , if e ≡ xi for some 1 ≤ i ≤ n, (4) f , if e ≡ fx1...xn , and xi oc / (5) Bn I i m f for all 1≤i≤n, m I n (A*x1...xn:f2)...(A*x1...xn:fm) , if e ≡ f1f2...fm , f1,f2,...,fm are primal components of e, and f1 ≡ xi for some 1 ≤ i ≤ n, m-1 (6) B n fl(A*x1...xn:f2)...(A*x1...xn:fm) , if e ≡ f1f2...fm , f1 is the longest initial component of e such that xi oc / f1 for all 1 ≤ i ≤ n, and f2,...,fm are primal components of e. Note that for cases (5) and (6), we decompose the ob form e into an initial component and a number of primal components, with the initial component chosen to be either a single indeterminate among x1,...,xn, if possible, or else the longest possible ob form not containing any of x1,...,xn. Example. To find A*xyz:e, where e is as defined in the previous example. Diagrammed below is the decomposition of e and its components according to the conditions prescribed in (5) and (6) above. 48 SK (x (KK) y z) (S (w z) (SS y)) (x y z) _ _ __ _ 1 2 1 2 _ ___ _ _ _ _____ ______ 1 2 3 4 1 2 3 __ _______________ __________________ ________ 1 2 3 4 Hence, A*xyz:e 3 4 1 2 2 3 1 3 1 2 ≡ B 3 (SK)(B 3 II 3 (K3(KK))I 3 I 3 )(B 3S(B 3 w I 3 )(B 3 (SS)I 3 ))I . By using Rules 4-10, it can be easily verified that, except for parts (3b) and (4), Theorem 2-7 remains valid when R A is replaced by A*. It is possible to restore (3b) by a slight modification in the above definition of abstracts. Let A**x1...xn:e be specified similarly to A*x1...xn:e, except for modifying the clause (4) in Definition 4-16 to (4) A**xl...xm-l:f, if for some 1 ≤ m < n, e ≡ fxm...xn , and for m ≤ i ≤ n, xi oc / f . (We consider A** xl...xm-l:f to be simply f when m = 1.) Let e be the same ob form as before. 3 2 1 Then, we have 2 1 2 A**xyz:e ≡ B 3 (SK)(B 1 II 1 (K1(KK)))(B 3 S(K2w)(B 3 (SS)I 3 ))I . It can be verified that with A** used instead of A except (4) of Theorem 2-7 remain valid. R , all parts 49 In the sequel, we will drop the superscript on A altogether, thereby leaving the abstraction algorithm unspecified. A function on natural numbers is partial recursive if it can be defined using the following functions and function-forming schemes [14]: 1. Successor Function S(x) = x+1 . 2. Constant Functions C q (x1,...,xn) = q, q a natural n number. n 3. Identity Functions U i (x1,...,xn) = xi, 1 ≤ i ≤ n. 4. Composition Scheme: Given functions g,h1,...,hm, to obtain f such that f(x1,...,xn) = g (h1(x1,...,xn),...,hm(x1,...,xn)). 5. Primitive Recursion Scheme. Given functions g and h, to obtain f such that f(x1,...,xm,0) = g(x1,...,xm) , f(x1,...,xm,y+l) = h(x1,...,xm,y,f(x1,...,xm,y)). 6. Minimalization Scheme. Given a function g, to obtain f such that f(x1,...,xm) = (µy)[g(x1,...,xm,y) = 0] = the least integer y, if one exists, such that g(x1,...,xm,y) = 0 . Now the obs suc, Kn q and i I n clearly represent the first three functions of the above list. Further, if the obs 50 g and hi are the representations of the functions g and hi, m respectively, then f ≡ B n gh1...hm represents the f of Scheme 4. Thus, to complete the combinatory representation of partial recursive functions, it only remains to provide the obs primrecm and mnmlzm such that the definitions f ≡ primrecm h g and f ≡ mnmlzm g may correspond to Schemes 5 and 6. For this purpose, we give the following definitions and rules, adapted from Petznick [29] with minor modifications. (4-17) Definition. (1) primrec0 ≡ Axy:<Az:<suc(zK),x(zK)(xZ)>,<0,y>,Z> , 2 (2) primrecm ≡ B 4 primmrec0 , m ≥ 1 , (3) mnmlz0 ≡ D(Axyz:zy(K(xx(suc y)z))y)0 , (4) mnmlzm ≡ mnmlz0(m) (4-18) (≡ m B mnmlz0) , m ≥ 1 . Rules. (1) primrecm abc1...cmn bc1...cm , if n = 0, → ac1...cmn-l(primrecmabc1...cmn-l), if n > 0. (2) mnmlzmab1...bm → n , provided that ab1...bm → 0, and, for all 0 ≤ p < n, there exists some q > 0 such that ab1...bmp → q . 51 We shall make use of certain special forms of recursion in specifying some ob sequences. It is possible to give individual, explicit definitions for the obs of these sequences. We indicate in the table below how all members of such ob sequences can be generated from a single ob, thus avoiding the need to postulate infinitely many primitives in an extended calculus containing such obs. (4-19) Table. Specification of the Definition of the ob f ob sequence fi _____________________ such that f i → fi _______________________ (1) f0 ≡ g , fn+l ≡ h fn . f ≡ <h,g> . (2) f1 ≡ g , fn+l ≡ h fn . f ≡ <h,g>°e pred . (3) f0 ≡ g , fn+l ≡ h n . f ≡ C(SI(K°(h°pred)))g . (4) f0 ≡ g , fn+l ≡ h n fn . (5) f0 ≡ g , f ≡ primrec0 h g . f ≡ primrec0 <B,h>g . fn+la1...an+1 ≡ h(fna1...an)an+1 . (6) f0 ≡ g, f ≡ primrec0(C°(CBh°TB))g. fn+la1...an+1 ≡ ha1(fna2...an+1) . 52 For each entry in the Table 4-19, the relation fn → fn can be verified from the definition of f. prove the case of entry (3). f ≡ C(SIp)g. For example, we Let p ≡ K°(h°pred), so that Then: f0 ≡ C(SIp)g0 → SIp0g → I0(p0)g → 0(p0)g → g ≡ f0. fn+1 ≡ C(SIp)gn+1 → SIpn+1g → In+1(pn+1)g → n+l(pn+1)g → pn+l(n(pn+1)g) ≡ (K°(h°pred))n+l(n(pn+l)g) → K(h(pred n+l))(n(pn+lg) → hn ≡ fn+l . To facilitate the representation of a succession of function applications, we introduce the ob family nestn: (4-20) Definition. nest0 ≡ I , nestn+1a1...an+l ≡ Ba1(nestn a2...an+l) , [a1,...,an] ≡ nestn a1...an . (4-21) Rule. [a1,a2,...,an]b → a1(a2(...(an b)...)) . Note the following properties of nests: [a,...,a] ↔1 n a , ↑ n occurrences [a1,...,am,b1,...,bn] ↔1 [a1,...,am]° [b1,...,bn] . 53 The purpose of the next set of obs is to represent functional operators for permuting and duplicating the arguments of functions. These obs thus generalize the effect of C and W. (4-22) Definition. (1) rotl1 ≡ I , rotln+1 ≡ (BC°B) rotln , (2) rotr1 ≡ I , rotrn+1 ≡ C(B°B)C rotrn , n ≥ 1. n ≥ 1. m (3) swap n ≡ [rotrm , rotrn , rotlm+1 , rotln] , l≤m≤n . m n i i (4) perm i1,...,im ≡ B n I I 1 n ... I m n , l ≤ ij ≤ n for all 1 ≤ j ≤ m. (5) dup0 ≡ I , dupn+1 ≡ dupn°(W(n+1)rotrn+1)(n+1) , n ≥ 1. (4-23) Rule. (1) rotln a b1b2...bn → a b2...bn b1 , n ≥ 1 . (2) rotrn a b1...bn-1bn → a bnb1...bn-1 , n ≥ 1 . m (3) swap n a b1...bn → a b1...bm-1bnbm+1...bn-1bm l ≤ m ≤ n . (4) n perm i1,...,ima1...an → ai1...aim , l ≤ ij <n for all l ≤ j ≤ m . (5) dupn a b1...bn → a b1... bnb1... bn . , 54 Proof. (1), (2), and (5) by induction on n, (3) by (1) and (2), and (4) by Rule 4-10 (3). Our representation of ordered tuples is adopted from Church [8]. The essential idea is to regard the tuple <a1,...,an> as the abstract Ax: xa1... an , that is, to define tuples so as to obtain the rule <a1,...,an>b → ba1...an . (Two special cases of ordered tuples, namely, pairs and triples, and their associated rules were introduced earlier (4-7 and 4-8). In addition to the tuple-forming operators, we will require a number of obs to represent various manipulations on tuples, such as inserting, retrieving, or changing elements. Here are the necessary definitions and rules. (4-24) Definition. (1) tup0 ≡ I , tupn+1 al...an+l ≡ C(tupn a1...an)an+1 , <a1,...,an> ≡ tupn a1...an , n ≥ 0 . (2) insert ≡ C . m m (3) elem n ≡ I n , l ≤ m ≤ n . m (4) replace n ≡ [rotlm+1,rotrm,K]tupn, 2 m m (5) fput n ≡ B n I(rotrn+1 replace n ) , (6) m m funtup n = B n tupn , l ≤ m ≤ n . l ≤ m ≤ n . l ≤ m ≤ n . 55 (4-25) Rule. (1) <a1,...,an>b → ba1...an . (2) insert <a1,...,an>b → <a1,...,an,b> . (3) elem n a1...an → am , (4) replace n b a1...an → <a1,...,am-1,b,am+1,...,an> , m l ≤ m ≤ n . m l ≤ m ≤ n . m <a1,...,an> (replace n b) → <a1,...,am-1,b,am+1,...,an> , l ≤ m ≤ n . m (5) fput n fa1...an → <a1,...,am-1,fa1...an,am+1,...,an> , l ≤ m ≤ n . m <a1,...,an> (fput n f) → <a1,...,am-1,fa1...an,am+1,...,an> , l ≤ m ≤ n . (6) Proof. m funtup n f1...fm a1...an → < f1a1...an,...,fma1...an>. Omitted We claim that there exist obs nest, rotl, rotr, tup, elem, and replace possessing the following reduction properties: 56 nest n → nestn , n ≥ 0 , rotl n → rotln , n ≥ 1 , rotr n → rotrn , n ≥ 1 , tup n → tupn , n ≥ 0 , m e1em m n → elem n , l ≤ m ≤ n , m replace m n → replace n , l ≤ m ≤ n . The definitions of nest, rotl, rotr, and tup follow directly from Table 4-19 and Definitions 4-20,22,24; in addition, we can take elem ≡ I , replace ≡ Axy: [rotl (suc x), rotr x, K] (tup y) . It should be noted that, indeed, each ob sequence that we have introduced so far, or will introduce in the sequel, can be generated from a finite number of obs. CHAPTER 3 BASIC PROGRAMMING FEATURES In this chapter, we undertake the representation of the more elementary features of high-level programming languages, postponing the discussion of jumps and procedures to later chapters. We use the ALGOL 60 [27] terminology and notation, whenever possible, to express programming language features. 3.1 An Overview To construct our model, we start with the representations of the atomic constituents of program, such as constants and variables. We then develop the rules for obtaining the representations of larger and larger programming constructs by combining the representations of their syntactic units in certain ways, eventually deriving the rules for representing whole programs. Throughout this development, we are guided by intuitive interpretations of programming constructs as functions. There are several different ways in which a program may be regarded as a function, depending upon what we consider to be the arguments of the program and what we regard as the finally computed results. These different functional 57 58 interpretations of programs may result in different choices of the representations of individual programming constructs. We will take the view that it is the external input-output behavior that most appropriately characterizes a program, and choose our representations with the goal of making this behavior as explicit as possible. Consider, for example, the program: begin integer a,b,c; read a; read c; b := a+c; write b; b:=b-2xc; write b end (We use read and write as standard statements for performing single-item input-output operations). As far as the external input-output is concerned, the above program behaves like a two-argument function which produces as value two quantities, the sum and difference of its arguments. Thus, this program may be intuitively interpreted as the function f given by f(x,y) = <x-y,x+y> . (i) The function f is representable in SK by an abstract f ≡ Axy:<x-y,x+y> (ii) having the reduction property fxy → <x-y,x+y> . (iii) 59 (Although the expressions x-y and x+y are not exactly ob forms, they can be easily translated to be such -- as we will soon see.) Accordingly, we would like to set up the model in such a manner that the representation of the above program may turn out to be an ob f, satisfying (iii). So constructed, the model would, in essence, enable us to abstract out of a program code the function from the input space to the output space that the program computes. For, f represents precisely this input- output function. More generally, let P be a program, i1,...,ip, its inputs, and o1,...,oq its outputs. Then we would like that the representations provided by our model satisfy the reduction relation (1-1) {P}{i1 }...{ip } → <{o1 }...{oq }> , in which the representations are denoted (anticipating a forthcoming notation) by enclosing within braces the symbols for the corresponding represented entities. The subsequent sections will show how the representations of various programming constructs may be chosen to fulfill the above requirement. 60 3.2 Variables For each program that we wish to represent in our model, we shall need a fixed correspondence between the variables declared in the program and the indeterminates of (the augmented) SK. Now in block-structured programming languages, it is permissible to employ the same identifier to denote different program variables as long as those variables have different scopes. In choosing the correspondence between program variables and indeterminates, it will not be necessary to distinguish between any two identically denoted variables that are declared in disjoint blocks. But it will be necessary to distinguish all the variables that are declared in a set of nesting blocks. (To distinguish two such variables denoted by the same identifier, it suffices, for example, to superscript the identifier by the respective block level numbers -- zero for the outermost (program) block, and n+l for the blocks immediately enclosed by a block at level n.) The correspondence between program variables and indeterminates will be set up by assigning distinct indeterminates to distinct variables (in the above sense) in some order.1 1 Since we We shall assign single indeterminates to both simple variables and array variables, however, the treatment of arrays is deferred to a later section, and until that discussion all variables are meant to be simple. 61 have not specified the alphabet for indeterminates, we shall, for convenience in expressing our representations, denote the indeterminates by the same symbols by which the corresponding program variables are denoted. The representation of a programming construct in a given program depends upon, among other things, the variable declarations in whose scope the construct appears. To account for this, we need the notion of environment defined as follows: The environment of a construct in a program is a list of all the program variables that have been declared in the blocks enclosing the point at which the construct occurs. In this list, the variables are to be arranged in their order of declaration within individual blocks, with the blocks taken in the innermost to the outermost order.2 From what has been stated earlier about distinguishing program variables, it follows that the variables constituting an environment are all distinct. Example. Consider the following schematic program, in which it is assumed that the omitted statements indicated by ellipses do not contain declarations. 2 When the program contains procedures, the environments may also include formal variables and a number of other variables which are not explicitly declared in the program; these additional variables will be introduced in Chapter 5. 62 A:begin integer x,y; B:...; begin integer y,z; C:...; end; begin integer x,w,z; D:..., end end From our view-point, this program makes use of six 0 0 1 1 1 distinct variables, namely, x ,y ,y ,z ,x , and superscripts indicate block level numbers. 0 0 1 1 w , where the For simplicity, let 1 us omit the superscripts from x ,y ,z , and w . Then the environments of the statements labelled A (i.e., the whole program), B, C, and D are, respectively, the null list 1 1 (), (x,y), (y ,z,x,y), and (x ,w,z,x,y). In general, the SK representation of a construct depends upon the construct’s own environment as well as the environments of its constituents. We denote the SK representation of a construct X appearing in the environment E by {X }E. We drop the subscript from the above notation if the representation of X is the same in all environments (in which X 63 can legally occur). A formula specifying the representation of a construct in terms of its environment and the representations and environments of its constituents will be referred to as a representation rule. In such a formula, the environments of the constituents will generally be omitted if they are the same as the environment of the construct under representation. 64 3.3 Constants, Operations, Relations The general criterion for choosing the SK representations of the values of the various types employed in programming languages and associated operations and relations may be stated thus: Let * denote a unary operation and ** a binary operation or relation. Then for all operands a and b of the proper types for which * and ** are defined, it should be the case that {*}{a} → {value of (*a)} , {**}{a}{b} → {value of (a**b) . In this way, it becomes possible to interpret the computation of values as simply the SK reduction process. Of course, the above criterion may be met by several different representations, in which case the choice is dictated by the simplicity of the resulting obs. We begin with the Boolean values. The values true and false are represented by the obs K and Z, (Definition 2.4-1), respectively. The motivation behind this choice is that it leads to a very simple representation of conditional expressions; namely, {if b then c else d) ≡ {b}{c}{d} (i) Quite short representations of logical operators can then be provided by using McCarthy’s well-known technique of expressing these operators in terms of conditionals [23]. 65 In particular, we obtain: (2-1) Definitions and Rules. (1) true ≡ K , true a b → a . (2) false ≡ Z , false a b → b . (3) ¬ ≡ <false,true> , ¬ a → a false true . (4) ∧ ≡ CC false , ∧ a b → a b false . (5) ∨ ≡ T true , ∨ a b → a true b . (6) imp ≡ CC true , imp a b → a b true . (7) eqv ≡ CS ¬ , eqv a b → a b (¬ b) . It is an easy matter to verify that the above presented obs correctly represent the corresponding logical operators. For example, it is seen that imp true true → true true true → true imp true false → true false true → false imp false true → false true true → true imp false false → false false true → true So that a conditional expression may be recognizable as such even when represented in SK, we make the following trivial definition and restate the representation rule (1). (2-2) Definition. if ≡ I . (2-3) Representation Rule. {if b then c else d } ≡ if {b}{c}{d} . 66 Let us now turn to the representation of arithmetic in SK. We will only be concerned with integers and rational numbers. (The type called “real” in the programming parlance will be termed “rational” here.) The representation of natural numbers has already been described (Definition 2.4-4). Following the procedure common in analysis, one can consider integers and rational numbers to be equivalence classes of natural numbers and integers, respectively. For convenience in carrying out reductions with their representations, it seems preferable, however, to represent these numbers in terms of uniquely selected members of the equivalence classes they stand for. Thus, an integer p is traditionally defined to be the set of all pairs <m,n> of natural numbers in and n, such that, intuitively, p is a solution of the equation m = n+p; but we prefer to define the SK representation of p to be the representation of that particular pair in the set which has the smallest m and n (at least one of these being necessarily zero). Again, a rational number r is the set of all pairs <p,q> of integers p and q such that r intuitively satisfies p = q × r; but we prefer to represent r by the representation of that pair for which q is positive, p and q are relatively prime, and, furthermore, if p = 0, then q = 1. 67 Numbers of different types denoted by the same symbols in a programming language are in reality different entities possessing different ob representations. We denote them by underlined numerals, unsubscripted in the case of natural numbers, and with subscripts Z and Q added for integers and rational numbers, respectively. Thus, the obs 3, 3Z , and 3Q and represent the natural number 3, the integer 3 and the rational number 3. As discussed above, we define them as follows: 3 ≡ suc (suc (suc 0)) , where suc ≡ SB, 0 ≡ Z , 3Z ≡ <3, 0> , 3Q ≡ <3Z,1Z> ≡ <<3,0>,<1,0>> . Additional examples: 0Z ≡ <0,0>, 0Q ≡ <0Z,1Z> -3Z ≡ <0,3>, -3Q ≡ <-3Z,1Z> -2.6Q ≡ <-13Z,5Z > ≡ <0Z,13Z> ≡ <<0,0>,<1,0>> , ≡ ≡ <<0,3>,<1,0>> , <<0,13>,<5,0>> . Having settled upon the representation of numbers themselves, let us turn to the representation of operations and relations defined on numbers. We denote the representations by the corresponding algebraic symbols, again unsubscripted in the case of natural numbers and subscripted with the letters Z and Q in the case of integers and rational numbers, respectively. Thus, +, +Z, and +Q represent natural number, integer, and rational addition, respectively, and are to be so 68 defined that, for example, + 3 2 → 5 . +Z 3Z -2Z → 1Z , i.e., +Z <3,0><0,2> → <1,0> . +Q 5/6Q -4/9Q → 7/18Q . Obs representing the successor and predecessor functions on natural numbers were defined earlier (Definition 2.4-4,2.49). Using the method of representing recursively defined functions given in Section 2.4, the SK representations of other operations and relations on natural numbers can be obtained from their well-known recursive definitions [14]. For strong combinatory calculi, this is done in Church [8]; for the weaker SK reductions, some representations are given in Petznick [29). Finally, to represent integer and rational operations, we can make use of the definitions of these operations in terms of, respectively, the natural number and integer operations on the components of the pairs representing their operands. integer operations for example. Consider First we will require an ob normlZ to represent the “normalization” operation of converting an arbitrary pair of natural numbers in the equivalence class denoting an integer to the unique pair chosen for the CC representation of that integer; e.g., normlZ <l6,5> → <11,0> , Having represented the proper subtraction and minimum selection 69 operations on natural numbers by the obs -̇ and min, respectively, we would need the rule normlZ <m,n> → <-̇ m (min m n), -̇ n (min m n)> . In view of Rule 2.4-8(2), an adequate definition for this purpose is: normlZ ≡ Ax:< -̇ (xK)(min(xK)(xZ)), -̇ (xZ)(min(xK)(xZ))> . Now we can represent integer addition and subtraction (in terms of natural number addition on the components of the integer operands) by the obs defined to satisfy the reduction relations +Z <a,b> <c,d> → normlZ <+ a c , + b d> , -̇ Z <a,b> <c,d> → normlZ <+ a d , + b c> . As additional examples, let us consider the familiar programming operations of type-conversion from integers to rational numbers, and the “mixed addition” of rational and integer operands giving a rational result. The obs float and +QZQ representing these operations have to satisfy, for example: float 2Z → 2Q , +QZQ -3.14Q 2Z → -1.14Q . 70 The following definitions clearly suffice for these obs: float ≡ Ax:<x,lZ> , +QZQ ≡ Axy:+Q x(float y) . Not all operations, of course, are so easy to deal with. Furthermore, the operations such as the exponentiation to fractional powers, which lead to irrational numbers, can be represented in our scheme only by defining them in terms of functions that approximate the results to some desired precision, using the techniques for representing functions that will be described later. However, the actual details of representing various arithmetic operations and relations are not crucial to our model, for this model is not intended to be used in studying the purely numerical aspects of programs. The purpose of the discussion in this section is simply to indicate that it is possible to represent arithmetic in SK, and to sketch a way of carrying out this representation. 71 3.4 Expressions We limit ourselves at present to the expressions that do not contain function designators. (This restriction will be lifted when procedures are discussed.) According to the requirements imposed by programming languages, any variable occurring in such an expression must also occur in the environment of the expression. its environment. Let e be an expression and E Then, in view of the SK representations chosen for operations and constants, the representation {e}E will be defined by induction on the (parse) structure of e to be the following ob form: 1) If e consists of a constant represented by c, then {e}E ≡ c. 2) If e consists of a program variable corresponding to the indeterminate x, then {e}E ≡ x. 3) If e consists of a k-ary operation (or relation) represented by o, with the subexpressions el,...,ek as operands, then {e}E ≡ (o {e1}E ,..., {ek}E). It follows that the SK representation of an expression may be obtained simply by writing the expression in the prefix notation, with all operator-operands combinations parenthesized, and then replacing all operators and operands by their representations. It further follows that the 72 representation of an expression is the same in all environments in which it can legally appear. Thus, in accordance with a convention stated in Section 3.2, we may omit the mention of environment in denoting the representation of expressions. Consider, for example, the following expression in which x, y, z denote program variables: x + if y ≠ 0 then z+y else l5. (i) In the prefix form, this expression may be written as: (+ x (if (≠ y 0) (+ z y) 15)). (ii) In accordance with the conventions stated in Section 3.2, let us use the symbols x, y, z for indeterminates as well as the program variables. Now if the program variables have been declared to be all of type integer, and the value of the above expression is to be of type integer also, then the expression may be represented by the ob form +Z x (if (≠Z y 0Z) (+Z z y) 15Z). (iii) On the other hand, if the program variable x and the result are of the type rational, and y and z of type integer, then the expression may be represented by +Q x (float (if (≠Z y 0Z) (+Z z y) 15Z)). (iv) where the ob float represents the type-conversion operation 73 mentioned in the previous section. But for closer resemblance between the given expression and its representation, we find it preferable to write, instead of (iv), +QZQ x (if (≠Z y 0Z) (+Z z y) 15Z) , (v) with the ob +QZQ representing the “mixed” addition of a rational number to an integer, producing a rational number as value. For conciseness of notation in stating the representation of expressions, we shall henceforth assume that all typeconversions are absorbed within mixed operations as above. Furthermore, we shall omit all type indicating subscripts, leaving the types to be inferred from the context. For example, we shall abbreviate (v) to + x (if (≠ y 0) (+z y) 15) . (vi) No serious ambiguity will arise out of the above convention, because, except for using natural numbers in a few, explicitly indicated, instances, we shall use the numbers and operations of type integer only. We shall often need the abstract of the representation of an expression with respect to the indeterminates representing the variables in the environment of the expression. The procedure of Definition 2.4-16 is particularly easy to apply in this case. Specifically, for the special case of 74 expression representations, we may state the following (4-1) Abstraction Algorithm. Let e be the ob form representing an expression which appears in the environment (v1,...,vn). Then to obtain Av1... vn:e, rewrite e, replacing k (1) each ob o representing a k-ary operation by (B n o) i (2) each indeterminate vi, 1 ≤ i ≤ n, by I n , and (3) each ob c representing a constant operand by (Knc). Example. Let (x,y,z,w) be the environment of the expression if y = 218 ∧ x+3 < -z then z+(7+y) else if x × y ≥ z then entier (x/z) else -12. The above expression may be represented by the ob form e ≡ if (∧ (= y 218) (< (+ x 3)(minus z))) (+ z (+ 7 y)) (if (≥ (x × y) z) (entier(/ x z)) -12) , where minus and entier have obvious significance. Using the above procedure, we obtain 3 2 2 2 2 2 1 Axyzw:e ≡ B 4 if(B 4 ∧ (B 4 = I 4 (K4 2l8))(B 4 <(B 4 + I 4 (K43)) 3 1 2 3 2 2 (B 4 minus I 4 ))) (B 4 + I 4 (B 4 + (K4 7)I 4 )) 3 2 2 1 2 3 1 2 1 3 (B 4 if (B 4 ≥ (B 4 × I 4 I 4 )I 4 ) (B 4 entier (B 4 /I 4 I 4 )) (K4 -12)) . 75 It is only in the representation of expressions that we are compelled to employ indeterminates. In representing larger constructs, such as statements, which contain expressions as syntactic units, the expression representations will always be used as parts of abstracts of the above form, eliminating all indeterminates. 76 3.5 Assignments Before discussing any particular type of statement, we will first indicate the general idea behind our SK representations. Consider a given statement S of a program. Let (v1,...,vn) be the environment of S, and denote by F the section of the program following S and extending all the way to the program end. (F will sometimes be referred to as the program remainder of S.) The two parts of the program, one consisting of F alone, and the other composed of S and F together, may be interpreted as two functions φ and φ’, respectively, of the arguments v1,...,vn. With this interpretation in mind, the effect of the statement S is to transform φ into φ’. As the representation of S, therefore, we take precisely the function (to be accurate, the functional operator) σ given by (σ(φ))(v1,...,vn) = φ’(v1,...,vn) (i) which accomplishes the above transformation. Using the Schönfinkel interpretation of functions (Section 2.2), the above relation may also be written as σ(φ,v1,...,vn) = φ’(v1,...,vn) . (ii) Now suppose we can somehow express the right-hand-side of (ii) in terms of the function φ, the variables v1,...,vn, 77 and possibly some constants. Then we may take (ii) to be the functional equation defining σ, with v1,...,vn, and even φ, as formal arguments. (Note that while the domains of the arguments v1,...,vn are the values of the corresponding program variables, the domain of φ consists of the program remainders considered as functions.) Now by interpreting (ii) in combinatory terms, and by abstracting the ob form representing its right-hand-side with respect to the indeterminates φ,v1,...,vn, we may obtain a definition of σ as an ob. We remark that if the statement S is modelled as above by the ob σ, then the execution of S is modelled by the reduction of σ φ v1 ... vn , in which the symbols v1,..., vn denote the representations of the values of the corresponding variables immediately prior to the execution of S, and φ denotes the representation of the program remainder of S. A key step in representing a programming statement is to define a suitable equation of the form (i) or (ii) for it. (The choice of φ’ is, of course, based on our intuitive understanding of the effect of the statement.) For the sake of motivation, we will include the details of this step in describing the first few of our representations. 78 Let us now look at the assignment statement vi:= e in the environment (v1,...,vn). The φ’ in this case is obtained from φ by setting the argument vi to e. Thus, in effect, this assignment statement behaves as the function σ such that (σ(φ))(v1,...,vn) = φ’(v1,...,vn) = φ’(v1,...,vi-1,e,vi+1,...,vn) . In SK notation, this amounts to σφv1...vn → φv1...vi-1 {e}(v1,...,vn)vi+1...vn) . Accordingly, we adopt the following SK representation of assignment statements: (5-1) Representation Rule. {vi:= e }(v1,...,vn) ≡ Aφv1...vn: φv1...vi-1 {e}vi+1...vn) . (We recall from Section 3.2 the convention that in a representation rule, the environments of all representations are considered to be the same as that of the construct under representation, unless specified otherwise. Also, we assume in the above representation that any type conversion needed for the assignment has been incorporated within e itself.) It has been mentioned earlier that only the variables 79 contained in the environment of an expression can occur in the expression. Thus, no indeterminates other than v1,...,vn can occur in the ob form {e} in the above representation rule. It follows from the abstractions specified in that rule that the SK representation of an assignment statement is an ob, not an ob form containing occurrences of indeterminates. Note that the multiple assignments of ALGOL 60 [27] and the collateral (parallel) assignments of ALGOL 68 138] present no special problem. Omitting the formal rules for their representation, we simply illustrate their treatment in the following example. Example. Presented side by side below are some assignment statements and their SK representations. Note that the environment of the first two statements is (x,y), and of the 1 0 next three is (z,y ,x,y ); the superscripts in the latter environment are block level numbers, and are used to distinguish the two variables designated by the same identifier. Program Statement Representations begin integer x,y; x:=2; Aφxy:φ2y (or, Aφx:φ2) y:=x+l9; Aφxy: φx(+xl9) begin integer z,y; y:=x-5; x:=y:=z+(x+y) ; 1 0 0 Aφzy xy :φz(-x5)xy 1 0 1 1 0 Aφzy xy :φz(+z(+xy ))(+z(+x y ))y 80 1 0 0 Aφzy xy :φy1zxy (y:=z, z:=y); ... end end In order to express the representation of assignments explicitly as obs rather than abstracts, we introduce some new obs. Although their names are suggestive of the programming actions they represent, we emphasize that they are just ordinary obs, with no imperative notions attached to them. (5-2) Definition. 2 evalm ≡ C(B n I°rotrn+1) , n ≥ 1. 2 storei ≡ swap i+2K , i ≥ 1. i assign n ≡ CBstorei°evaln , n ≥ i ≥ 1. The following reduction properties are easy to derive. (5-3) Rule. evaln fφx1...xn → φ(fx1...xn)x1...xn storei φex1...xi → φx1...xi-1e . . i assign n fφ x1...xn → φx1...xi-1(fx1...xn)xi+1...xn Hence, we have the following alternative to (5-1). . 81 (5-4) Representation Rule. i {vi:= e }(v1,...,vn) ≡ assign n (Av1...vn:{e}) . Example. (x,y). Consider the statement y := x+19 in the environment In this case, we have {e} ≡ {x+19} ≡ +x19 , 2 1 Axy:{e} ≡ B 2+I 2 (K219) , by (4-1), 2 2 1 {y := x+l9}(x,y) ≡ assign 2 (B 2 + I 2 (K219)) . To conclude this section, we point out the fact that we have eliminated the concepts of memory and address from our model, and have reduced the concept of assignment to that of substitution or function evaluation. 82 3.6 Compound Statements Consider the compound statement S begin S1;S2 end appearing in the environment (v1,...,vn). of the program that follows S. Let F be the segment We can interpret the program segments F and (S;F) to be two functions φ and φ’, respectively, of the arguments v1,...,vn , and we are interested in the representation σ of S with the functional transformation property (σ(φ))(v1,...,vn) = φ’(v1,...,vn) . Now the execution of (S;F) has precisely the same effect as (S1;S2;F). Denoting by φ* the functional interpretation of the program segment (S2;F), and by σ1 and σ2 the representations of the statements S1 and S2 , respectively, we have (σ1(φ*))(v1,...,vn) = φ’(v1,...,vn) , (σ2(φ))(v1,...,vn) = φ*(v1,...,vn) . The above functional conditions can be expressed in SK as the following reduction relations: σ φ v1...vn → φ’v1...vn , σ1φ*v1...vn → φ’v1...vn , σ2φ v1...vn → φ*v1...vn , These relations will certainly hold if we choose 83 φ* ≡ σ2φ , φ’ ≡ σ1(σ2φ) , and hence, σ ≡ Aφ:σ1(σ2φ) . The generalization to the case of an n-component compound is now obvious. So we are led to the following SK representation of compound statements: (6-1) Representation Rule. {begin S1;S2;...;Sn end} ≡ Aφ:{S1}({S2}(...({Sn}φ)...)) or, ≡ [{S1),{S2},...,{Sn}] (where the notation [...] for nests is as introduced in Definition 2.4-20). Notice the convenient fact that in the above nest the individual statement representations appear from left to right in the same order in which the statements occur in the compound (cf. Stratchey [36]). Example. The representation of compound statements is illustrated below. Individual statement representations are shown on the same line as the statements (on the last line for multiple-line statements), and are given names for reference purposes. The environment is assumed to be (x,y). Statements Representations (i) begin x := 2; a ≡ Aφxy:φ2y 84 y := x+3; b ≡ Aφxy:φx(+x3) x := y+x c ≡ Aφxy:φ(+yx)y end d ≡ Aφ:a(b(cφ)) (ii) begin y := 5; e ≡ Aφxy:φx5 x := y+2 f ≡ Aφxy:φ(+y2)y end g ≡ Aφ:e(fφ) The compound statements (i) and (ii) are intuitively equivalent. model? How can their equivalence be demonstrated in our The obs representing the statements (i) and (ii), namely, d and g, must have the same interpretation as a function of the variables φ, x, and y. Alternatively, d and g must perform the same reduction when applied to the same obs φ, x, and y; that is, d φ x y ↔ g φ x y . (*) To verify (*), we reduce both sides to the same ob. g φ x y ≡ Aφ:e(fφ)φ x y → e(fφ) x y, since φ oc / e and φ oc / f, 85 ≡ (Aφxy:φx5)(fφ) x y → f φ x 5 ≡ (Aφxy:φ(+y2)y) φ x 5 → φ (+ 5 2)5 → φ 7 5 . Similarly, it is easy to see that d φ x y → φ 7 5 . In general, to show that two statements appearing in the same environment of n variables are equivalent, we need to prove that the SK representations f1 and f2 of those statements are mutually (n+1)-interconvertible (Definition 2.1-l1). however, is an unnecessarily strict condition. This, It is often the case that in all the intended executions of a program (that is, with the input data satisfying the program specifications), the values of program variables range over certain restricted domains only. In such cases, the equivalence of two statements in the environment of n variables may be established by proving that, for x1,...,xn representing not all arbitrary obs but only the possible values corresponding to the variables x1,...,xn of the environment, and for all obs φ, it is the case that f1 φ x1...xn ↔ f2 φ x1...xn . 86 3.7 Blocks Next, let us consider a block S whose head declares the variables u1,...,um and initializes these to the values3 c1,...,cm , and whose body consists of the statements S1,...,Sp, in that order. The execution of S can be broken down into three operations performed in succession: 1) Extension of the existing environment by the variables u1,...,um (initialized at c1,...,cm). 2) Execution of the compound begin S1;...;Sp end. 3) Deletion of the, variables u1,...,um from the environment. Let these three operations be denoted by the functions α, β, and γ. Let (v1,...,vn) be the environment of S. Then with the obvious significance of other symbols, we have (α(φ))(v1,...,vn) = φ(c1,...,cm,v1,...,vn) (β(φ))(u1,...,um,v1,...,vn) = (σ1(σ2(...(σp(φ))...))) (u1,...,um,v1,...,vn) (γ(φ))(u1,...,um,v1,...,vn) = φ(v1,...,vn) (σ(φ))(v1,...,vn) = (α(β(γ(φ))))(v1,...,vn) By expressing the above in SK notation, and making use of proper abstractions and simplifications, we obtain σ ≡ Aφv1...vn: σ1(σ2(...(σp(Au1...um:φ))...))c1...cmv1...vn . 3 We assume that the expressions c1,...,cm do not contain the variables u1,...,um; they may, however, contain the variables in the environment of S. 87 Consequently, we choose the following representation of blocks. (7-1) Representation Rule. {begin <type> u1 := c1;...;<type> um := cm; S1;...;Sp end}(v1,...,vn) ≡ Aφv1...vn:{Sl}F({S2}F(...({Sp}F(Au1...um:φ))...)) {cl}E...(cm}Ev1...vn , where E ≡ (v1,...,vn) and F ≡ (u1,...,um,v1,...,vn) . (We assume that the expressions ci include any needed typeconversions.) Using the notation of nests and tuples (Definitions 2.420, 2.4-24), an explicit combinatory description of the above abstract is [<{cl}E,...,(cm}E> , [{S1}F,...,{Sp}F ,Km]] In the case that the variables are left uninitialized in the block-head -- as is normal in ALGOL 60 -- any arbitrary ob can be used for {ci} in the above representation. One might wish to use for this purpose an ob which would play the role of the everywhere undefined function. This function is modelled, for example, by the ob Ω (Definition 2.4-1) having the property 88 Ω a → Ω for all a. It should be noted, however, that Ω does not possess a normal form. As a result, if Ω is used in place of the missing ci’s in (7-1), then the presence of any variables that remain undefined throughout the program execution would cause the program representation to behave as if the program contained an infinite loop.4 Being the representation of statements, the components {Si}F, 1 ≤ i ≤ p, of the right-hand-side of (7-1) do not contain any indeterminates. But being the representations of expressions in the environment (v1,...,vn), {ci}, 1 ≤ i ≤ m, may possibly contain v1,...,vn. If the variables declared in the block head are not initialized, then, by recourse to a suitable abstraction algorithm (Theorem 2.2-4(3)), the indeterminates v1,...,vn can be dropped from the right-hand-side of (7-1). We thus obtain the following simplified representation: (7-2) Representation Rule. {begin <type>u1;...;<type>um;S1;...;Sp end}(v1,...,vn) ≡ Aφv1...vn:{Sl}F({S2}F(...({Sp}F(Au1...um:φ))...))ΩΩ...Ω m times ↑ where F ≡ (u1,...,um,v1,...,vn) . 4 This situation may be avoided by using the ob Ω’ ≡ D(B(S(BSC))(BC(C(KD)))K instead of Ω. It is easy to verify both that Ω’ is normal and that, for all a, Ω’a → Ω’ . 89 Note that if only constants are used to initialize the declared variables, then again the variables vi can be dropped, and the representation is similar to (7-2), except that the constants are used instead of the corresponding Ω. Example. be (w). The environment of the following block is assumed to The individual statements and their representations are given side by side below. The representations have been given identifying names for reference purposes. Statements Representations begin integer x:=5,y; y := x-7; a ≡ Aφxyw:φx(-x7)w begin integer z; z := 3+y; b ≡ Aφzxyw:φ(+3y)xyw x := z × x c ≡ Aφzxyw:φz(×zx)yw end d ≡ Aφ:b(c(Az:φ))Ω end e ≡ Aφ:a(d(Axy:φ))5Ω Explicit combinatory definitions of the above obs, in accordance with our previous representation rules, are as follows: 2 2 1 2 2 2 1 a ≡ assign 3 (B 3 - I 3 (K37)) , 3 b ≡ assign 4 (B 4 +(K43)I 4 ) , 1 2 c ≡ assign 4 (B 4 x I 4 I 4 ) , 90 d ≡ [<Ω>,[b,c,K1]] , e ≡ [<5,Ω>,[a,d,K2]] . 91 3.8 Input-Output We shall assume for simplicity that the program input and output operations are each restricted to a single file. A file of items al,...,an will be represented by the tuple < {al},...,{an}> . (The empty file is represented by the null tuple < > ≡ I.) For given ob forms u and v, we will abbreviate the ob form insert u v by u,v ; also we will denote u,v,w by u,v,w , and so on. By Rule 2.4-25 (2), we have <xl,...,xn>,y ≡ <xl,...,xn,y> I,xl,...,xn ≡ <xl,...,xn> , , so that “,” may be regarded as the operation of writing on a file, and the file resulting from writing an item a on a given file b may be represented by {b},{a} . Now let S be a statement appearing in the environment v1,...,vn of a program, and let σ be the ob representing S. In our discussion so far, σ has been defined as an abstract of the form Aφv1...vn:... (*) with the indeterminate φ standing for the program remainder of S. Accordingly, the execution of S has been modelled by the reduction of the ob σ φ v1...vn , 92 in which the underlined symbols denote the representations of the values of the corresponding variables immediately prior to the execution of S. In order to take input-output into account, we will generalize the representations so as to model the above execution by the reduction of the ob (8-1) σ φ v1...vn w u1 u2...um , th with w denoting the output file and ui the i of the m items remaining on the input file at the moment of execution. (As soon as an item is read, it is supposed to disappear from the input file.) This arrangement requires that the representations of statements be generally of the form A φ v1 ... vn o i1 ... im : ... , where o,i1,...,im are the extra indeterminates corresponding to the output file and input items. It must be evident, however, that the representations of those statements which do not involve input-output can be simplified back to the form (*) by choosing abstracts properly. Furthermore, in the case of input-output statements, the following choice of SK representations is obvious: (8-2) Representation Rule. {read vj}(v1,...,vn) ≡ Aφv1...vno: φv1...vj-1ivj+1...vno , {write e}(v1,...,vn) ≡ Aφv1...vno: φv1...vn o,{e} where e is some expression to be output. , 93 In order to provide explicit ob representation of inputoutput statements, we introduce the following obs (8-3) Definition. j j+1 (1) read n ≡ swap n+3 K(n+1) , (2) writen ≡ 1 ≤ j ≤ n , 2 [B n+1 SK(n),K,(CC)(n)] . (8-4) Rule. j (1) read n φx1...xn o i → φx1...xj-1 i xj+1...xn o , (2) writen fφx1...xno → φx1...xn o,fx1...xn . Proof of (2). writen fφx1...xno 2 ≡ [B n+1 SK(n),K,(CC)(n)]fφx1...xno 2 → B n+1 SK(n)(K((CC)(n)f))φx1...xno , by Rule 2.4-21, → S(K(n)φx1...xn)(K((CC)(n)f)φx1...xn)o → K(n)φx1...xno(K((CC)(n)f)φx1...xno) → φx1...xn((CC)(n)fx1...xno) , by Rule 2.4-6, → φx1...xn(Co(fx1...xn)o) → φx1...xn(Co(fx1...xn)) ≡ φx1...xn o,fx1...xn . In view of the above rules, we propose the following alternative to (8-2): (8-5) Representation Rule. j {read vj}(v1,...,vn) ≡ read n , {write e}(v1,...,vn) ≡ writen (Av1...vn:{e}) . 94 3.9 Programs Let the input file initially presented to a given program consist of items i1,...,ip , and let o1,...,oq constitute the items of the final output file produced by the program. As remarked in Section 3.1, we wish to choose a program representation so as to obtain the relation {program }{i1 }...{ip } → < {o1 },...,{oq }> . (i) Now the execution of a particular statement of the program is modelled by the reduction of an ob given by (8-1) in the previous section. Suppose that as an instance of such a statement we take the entire outermost block of the program. Recalling the significance of symbols used in connection with (8-1), we obtain the following conditions: σ ≡ {program block } n = 0 as the environment is null , w ≡ I , as the output file may be considered empty at the start of the program , m = p , and uj = ij , l ≤ j ≤ p. Furthermore, in place of φ , the “null” program remainder, we may arbitrarily choose to employ the ob I. On substituting these values, the execution of the program is seen to amount to the reduction of the ob {program block } I I {i1 }...{ip } . (ii) 95 Next, consider (8-1) again -- but this time for the case when the entire program has been executed. Now we have: σ ≡ I, the null program segment, n = 0 , as the environment is null, w = < {o1 },...,{oq }> , representing the final output file, m = 0 , assuming the program exhausts the input file, φ ≡ I. Thus, (8-1) in this case becomes the ob I I < {o1 },...,{q } > , which reduces to < {o1 },...,{q } > . (iii) If our representations work properly, then the ob (ii) should reduce to the ob (iii); that is, {program block }I I {i1 }...{ip } → < {o1 },...,{oq }>. (iv) Comparing (i) and (iv), we obtain: (9-1) Representation Rule. {program} ≡ (program block) I I . (9-2) Remarks. (1) From (iv) and (9-1) it follows that < {i1 },...,{ip } > {program} → < {o1 },...,{oq }> , 96 that is, <input file> [program} (2) → <output file> . In the ob representing a program, the component ({program block} I) will be found to be of interest by itself; we will refer to it as the routine of the program. Example. Following is the representation of the simple program mentioned at the beginning of Section 3.1. Statements Representations begin integer a,b,c; read a; f ≡ Aφabcoi:φibco read c; g ≡ Aφabcoi:φabio b := a+c; h ≡ Aφabc: φa(+ac)c write b; j ≡ Aφabco:φabco,b b := b-2xc; k ≡ Aφabc:φa(-b(×2c))c write b j end m ≡ Aφ:f(g(h(j(k(j(Aabc:φ))))))ΩΩΩ Since the ob in represents the program block, the representation of the whole program is p ≡ mII. Now it can be verified that, for all integers a and b, p a b → <a+b,a-b> . Thus the program representation p indeed abstracts out the input-output behavior of the program (cf. Section 3.1). 97 The execution trace of the above program, when run with the integers 5 and 3 as data items, is reflected in the following SK reduction. p 5 3 ≡ m I I 5 3 → f(g(h(j(k(j(Aabc:I))))))Ω Ω Ω I 5 3 → g(h(j(k(j(Aabc:I))))) 5 Ω Ω I 3 → h(j(k(j(Aabc:I)))) 5 Ω 3 I → j(k(j(Aabc:I))) 5 (+ 5 3) 3 I → j(k(j(Aabc:I))) 5 8 3 I → k(j(Aabc:I)) 5 8 3 I,8 ≡ k(j(Aabc:I)) 5 8 3 <8> → j(Aabc:I) 5 (-8 (× 2 3)) 3 <8> → j(Aabc:I) 5 2 3 <8> → (Aabc:I) 5 2 3 <8>, 2 → (Aabc:I) 5 2 3 <8,2> → I <8,2> → <8,2> . 3.10 Conditional Statements Recall that the SK representation of a Boolean expression b has the property {b} p q → p , if b has the value true, → q , if b has the value false. In view of the above property, we choose the representation of a two-branch conditional statement as follows: (10-1) Representation Rule. {if b then S1 else S2 }(v1,...,vn) ≡ Aφv1...vn: {b}{S1}{S2}φv1...vn . 98 For the purpose of representation, a one-branch conditional statement (an if statement, in ALGOL 60 terminology) may be viewed as a two-branch conditional with a dummy or “donothing” statement for the second branch. When appearing in the environment (v1,...,vn), the “do-nothing” statement can obviously be represented by Aφv1...vn: φv1...vn that is, I. , Substituting the “do-nothing” statement for S2 in (10-1), we obtain the (10-2) Representation Rule. {if b then Sl}(v1,...,vn) ≡ Aφv1...vn:{b}{S1}Iφv1...vn . In order to describe the above representations explicitly as obs, we first introduce a new ob sequence and its associated reduction rule. (10-3) Definition. (10-4) Rule. 3 condn ≡ β°(B n I). condn bs1s2φx1...xn → bx1...xn(s1φx1...xn)(s2φx1...xn) . It is easy to see that if b is a Boolean expression, then for all ob forms p, q, and r, the following interconvertibility relation holds: {b}pqr ↔ {b}(pr)(qr) Applying this relation to (10-1), we may . 99 obtain the following alternatives to (10-1) and (10-2). (10-5) Representation Rule. (1) {if b then S1 else S2 }(v1,...,vn) ≡ Aφv1...vn: {b}({S1}φv1...vn)({S2}φv1...vn) ≡ condn (Av1...vn: {b}{S1}{S2} . (2) {if b then Sl}(v1,...,vn) ≡ Aφv1...vn: {b}({S1}φv1...vn)(φv1...vn) ≡ condn (Av1...vn: {b}{S1}I 3.11 . Arrays Arrays can be interpreted as tuples and combinations of tuples. An array of a single dimension is represented by a tuple of the representations of the individual array elements, taken in the order of the lowest to the highest subscript. An array of dimension n+1 is represented by a tuple whose elements are the representations of the n-dimensional subarrays (or slices, in the ALGOL 68 terminology [38]) obtained by fixing the first subscript in turn from the lowest to the highest possible value. For example, the array A [l:2, 1:3] is represented by < < {A11},{A12},{A13}> , < {A21},{A22},{A23}> > , where (Aij} is the representation of the array element Aij. in the case of simple variables, an array identifier can As 100 itself be used for the indeterminate assigned to the array variable. With the above interpretation of arrays, we next describe the representation of subscripted variables in expressions, assignments to subscripted variables, and array declarations. In this description, we assume for simplicity that all arrays have the lowest subscript bound of 1. To obtain the correct representation in the case of an array one of whose subscript bounds, l, is different from 1, one simply needs to first increment the corresponding bound and subscript expressions throughout the program by 1-l. 1) Subscripted variable as an operand in an expression The representation in this case is just the corresponding element of the tuple representing the array. Thus, given the declaration <type> array v [1:n], we have, on the basis of Rule 2.4-25 (3), i {v [i]} ≡ v elem n . This representation is inadequate, since, in general, n and i are given as expressions rather than constants, and their values may not be known at the time of SK translation of the program. However, we have seen (cf. end of Section 2.4) that there exists an ob elem such that for all obs a and b if a → i and b → n, where i, n represent natural numbers i, n such that 101 1 ≤ i ≤ n, i elem a b → elem n . Hence, given the array declaration v [1:e], we specify {v [f]} ≡ v(elem [f}{e}) . More generally, for the array v [1:e1,...,1:em], we have {v [f1,...,fm]} ≡ v(elem {f1}{e1})...(elem {fm}{em}). 2) Assignments to subscripted variables In this case, the representation consists in replacing the designated element of the tuple representing the array with the representation of the new value. arrays of a single dimension only. Let us first consider the We have already seen (Section 2.4, end) that there exists an ob replace such that, for all natural number representations i, m such that 1 ≤ i ≤ m and for all ob forms a1,...,am,b, <a1,...,am>(replace i m b) → <a1,...,ai-1,b,ai+1,...,am> . Hence, given the declaration <type> array vj[1: e], we have {vj[f] := g}(v1,...,vn) ≡ Aφv1...vn: φv1...vj-1(vj(replace {f}{e}{g}))vj+1...vn . The representation in the case of a higher dimensional array involves the replacement of all slices of the array 102 that are affected by the assignment. It is easy to see that, when the array vj is declared to be of bounds [l:e1,...,l:em], the following is a suitable representation: {vj (f1,...,fm] := g } ≡ Aφv1...vn: φv1...vj-1hvj+1...vn , where h ≡ vj(replace {f1}{e1}( vj((elem {f1}{e1}) (replace {f2}{e2}( vj((elem {f1}{e1})(elem {f2}{e2}) (replace {f3}{e3}( ... vj((elem {f1}{e1})...(elem {fm-1}{em-1}) replace {fm}{em}{g}))... ))) ))) )) . 3) Array declaration We treat array declarations in the same way as the declaration of simple variables with respect to environments and the representation of initialized variable values. However, there is the following exception: if an array is not initialized at the time of declaration, the block representation is obtained by assuming all the array elements to be Ω. Thus, for an array with the bound pairs [1:2,1:3], the initial value is represented by <<Ω,Ω,Ω>,<Ω,Ω,Ω>>. 103 Since, in general, array bounds may be specified by expressions, we need to create tuples of arbitrary dimensions and sizes in which all elements are Ω. This will be possible by means of the ob tupinit having the property tupinit 1 m → <Ω,Ω,...,Ω> , ↑ m elements tupinit n+l m1...mn+1 → <a,a,...,a> , ↑ m1 elements (*) where a ≡ tupinit n m2...mn+1 . In order to define tupinit, we need the following definition, making use of some obs of Section 2.4: (11-1) Definition. (11-2) Rule. maketup ≡ β(TW)pred tup . maketup n x → <x,..,x> , for integer n > 0. ↑ n times (11-3) Definition. tupinit0 ≡ Ω , tupinitn+1 a1a2...an+1 ≡ maketup a1 (tupinitn a2...an+1) . Now from Table 2.4-19 we can define an ob such that for all natural numbers n ≥ 0, tupinit n → tupinitn . 104 It is quite straightforward to check that with this choice of tupinit, the relations (*) are indeed satisfied. Example. The array representations discussed above are illustrated on the following block which is assumed to occur in the environment (n). Statements Representations begin integer array p [1:n], q [1:2,1:3]; integer r; r := q [n,n+1]; a ≡ Aφpqrn:φpq(q(e1em n 2) (elem(+ n 1)3))n p [r]:= r+3 b ≡ Aφpqrn:φ(p(rep1ace rn(+ r 3)))qrn end σ ≡ Aφn:a(b(Apqr:φ)) (tupinit 1 n) <<Ω,Ω,Ω>,<Ω,Ω,Ω>>Ωn CHAPTER 4 ITERATION AND JUMP STATEMENTS 4.1 Recursive Specification of Obs In dealing with program loops, we shall need obs having the property that they are equiform to one or more components of certain obs to which they reduce. That is, these obs are to possess given reduction properties of the form F → ...F...F... . (i) We refer to such obs as recursively specified obs, and describe two approaches to define these. One approach to define recursively specified obs is to admit them as primitive obs, taking the respective properties required of them as the reduction rules associated with them. Since the reductions rules so added are recursive, in the sense that they reduce an ob in terms of itself, it is not at all obvious that the Church-Rosser property would hold in the extended calculus. But it follows from the work of Rosen [32] that the Church-Rosser property is indeed preserved by such extensions, and, consequently, most other properties of reduction, such as the uniqueness of normal forms and the correctness of the standard-order reduction algorithm, also continue to be valid. 105 106 Another approach to defining the obs specified by the properties of the form (i) is to look for solutions of (i), treating such formulas as reduction relations involving an unknown. Now, in general, (i) may be satisfied by more than one solution, so that we may have the choice of different explicit definitions for the same ob. There is, however, no reason to expect that these different definitions of an ob are compatible to each other or to the definition of the ob as a new primitive -- compatible in the sense that all reduction sequences, which start with an ob having a recursively specified ob as a component and which use the different definitions of the recursively specified ob, yield the same normal form (if any). In fact, incompatibilities do occur, as the following trivial example indicates: Let F be an ob specified by F → F, and let it be required to reduce the ob G ≡ SF. By the definition of reduction (Definition 2.1-4), the given property of F is satisfied by every ob. In particular, with F ≡ S and F ≡ K chosen as two possible definitions of F, the same ob G may be reduced to SS in one case and to SK in another! Moreover, both these results are in conflict with the one obtained by taking F as a primitive ob and F → F as the associated reduction rule. For, then, G does not even have a 107 normal form! Thus, not all solutions of (i) are acceptable for a definition of the ob F. Following Morris [26], to characterize those solutions of (i) for which the resulting definitions of the ob F are compatible with the definitions of the first approach (of taking (i) as a reduction rule), we may proceed thus. Let us introduce a partial order on obs as follows: For obs a and b we say that a is extended by b, in symbols, a ≤ b, if, for all obs c, it is the case that ca ↔ cb whenever ca possesses a normal form. For example, it can be shown that Ω ≤ b for all obs b, where Ω is as given in Definition 2.4-1. Now, the particular solutions of (i) that we are interested in have the property that they are extended by all solutions of (i). In other words, for an explicit definition of the ob F specified by (i), we can take a minimal solution of (i) (with respect to ≤). For example, consider the relation F → F again. Since this relation is satisfied by all obs, Ω is a minimal solution for it. Thus, F ≡ Ω may be taken as a definition of the ob specified by F → F. Under this definition of F, the ob G ≡ SF does not possess a normal form. This agrees with the result obtained by taking F as a primitive ob, with F → F as its associated reduction rule. To obtain an explicit definition of the ob F given by (i), we may proceed as follows: Let x be an indeterminate, 108 and let H ≡ Ax: ...x...x... be an abstract with respect to x of the ob form obtained from the right-hand-side of (i) by replacing the components equiform to F by x. Now YH → H(YH) , by Rule 2.4-1(18), → ...(YH)...(YH)... , Hence, YH is seen to be a solution of (i). by Theorem 2.2-4. It has been shown by Morris [26] that this solution is also minimal. Consequently, F ≡ YH ≡ Y(Ax:...x...x...) (ii) is an explicit definition of the ob specified by (i). In general, (i) has infinitely many, mutually noninterconvertible, minimal solutions, which are, however, equivalent in the sense that they all have the same intuitive interpretations as functions. The choice of any one of these for the explicit solution of (i), such as YH in (ii), is rather arbitrary. To leave this choice unspecified, while emphasizing the minimality of the chosen solution, one may employ the µnotation of deBakker [3]. In this notation, the minimal solution of (i) is designated by the µ-expression µx:...x...x... , 109 where, the ob form to the right of the colon is obtained from the right-hand-side of (i) by replacing F with the indeterminate x. Since the formula (i) has the appearance of a relation, which may not necessarily suggest that it is intended to define anything, we shall use the notation F ≡ ...F...F... to indicate that the ob F is being defined as specified by (i). The above treatment of recursively specified obs can also be generalized to include the simultaneous recursive specification of several obs, such as F1 → H1F1...Fn , ... (iii) Fn → HnF1...Fn , where F1,...,Fn do not occur as components in H1,...,Hn. The definitions of F1,...,Fn may be obtained as follows: 1) The F’s specified by (iii) are considered primitive obs whose associated reduction rules are just the formulas (iii). 2) The F’s specified by (iii) may be explicitly defined as the minimal solutions of the system of 110 reduction relations (iii). An explicit solution may be obtained as follows: Consider <H1F1...Fn,...,H1F1...Fn> n ← funtup n H1...Hn F1...Fn, by Rule 2.4-25, n ← <F1,...,Fn>(funtup n H1...Hn) n ← <funtup n H1...Hn><F1,...,Fn> . Hence, we may take, for 1 ≤ i ≤ n, n i Fi ≡ Y <funtup n H1...Hn> elem n . As before, we shall employ the notation F1 ≡ H1F1...Fn , ... Fn ≡ HnF1...Fn , to indicate the definition of the obs F1,...,Fn by means of the formula (iii). 111 4.2 Iteration Statements The representation of the for statement of ALGOL 60 is obtained by expressing this statement in terms of the simple (non-ALGOL 60) while loop of the form while ... repeat... . To represent the latter, consider the statement while b repeat S appearing in the environment (v1,...,vn). Calling this statement by the name T, we may (recursively!) describe it, for the purpose of SK representation, as if b then begin S;T end . Now the formulas for the representation of compound and conditional statements, (3.6-1) and (3.10-5(1)) respectively, are applicable to the above statement, so that its representation {T} is, recursively, the ob Aφv1...vn: {b}((Aφ: {S}({T}φ))(φv1...vn) (φv1...vn) ↔n+1 Aφv1...vn: {b}({S}({T}φ))φv1...vn . Thus, we adopt the (2-1)Representation Rule. {while b repeat S}(v1,...,vn) ≡ µx: Aφv1...vn: {b}({S}(xφ))φv1...vn . Alternative definitions of the same ob, call it X, are X ≡ Aφv1...vn: {b}({S}(Xφ))φv1...vn , X ≡ Y(Axφv1...vn: {b}({S}(xφ))φv1...vn) , X ≡ condn {b} [{S},X] I . 112 Example. At this point, we illustrate the SK representations introduced so far by means of a complete program. Also, as an application of the model, we derive the correctness of the program in terms of its representation. Given below are the individual statement representations, shown on the same line as the statements (or on the last line for multiple line statements), and have been designated names for reference purposes. begin integer x,y; read x; a ≡ Aφxyoi:φiyo y := 0; b ≡ Aφxy:φx0 begin integer z; z := 0; c ≡ Aφzxy:φ0xy while z < x repeat begin y := l+y+2×z; d ≡ Aφzxy:φzx(+(+1y)(×2z)) z := z+l e ≡ Aφzxy:φ(+z1)xy end end while f ≡ Aφ:d(eφ) g ≡ Aφzxy:(<zx)(f(gφ))φzxy h ≡ Aφ:c(g(Az:φ))Ω write y end j ≡ Aφxyo: φxy o,y k ≡ Aφ: a(b(h(j(Axy:φ))))ΩΩ {program ≡ P ≡ kII 113 We wish to prove that on reading a nonnegative integer n, 2 this program will print out the integer n . According to our input-output conventions, we need to show that 2 P n → <n > , for all integers n ≥ 0 . (i) This is done in four steps, as follows: (a) We show that, for all obs φ, and all integers n and i, g φ i n i 2 g φ i n i 2 2 → φ i n i , → g φ i+1 n (i+1) 2 if i ≥ n, (ii) , if i < n . (iii) By the definition of g, we obtain g φ i n i 2 2 → (< i n) (f(gφ)) φ i n i . If i ≥ n, then (< i n) → false, so that (ii) is immediate. Otherwise, (< i n) → true, and the above ob 2 → f(gφ)i n i 2 → d(e(gφ))i n i → gφ i+1 n (i+1) 2 2 → e(gφ)i n(+(+ 1 i )(× 2 i)) . (b) Next, for all integers n and i such that 0 < i ≤ n, we have 2 g φ 0 n 0 → g φ i n i . This is proved by induction on i. (iv) From (iii) one easily verifies (iv) both for i = 1, and for i = j+l ≤ n when the case for i = j < n is assumed. 114 (c) Next, we claim that for all integers n ≥ 0, it is the case that 2 h φ n 0 → φ n n . (v) For, we have h φ n 0 ≡ (Aφ:c(g(Az:φ))Ω)φ n 0 → c(g(Az:φ))Ω n 0 → g(Az:φ) 0 n 0 . Now if n = 0, then from (ii) it follows that 2 g(Az:φ) 0 n 0 → (Az:φ) n n n → φ n n 2 . On the other hand, if n > 0, then for the case i = n (iv) yields 2 g(Az:φ) 0 n 0 → g(Az:φ) n n n 2 → (Az:φ) n n n 2 → φ n n (d) by (ii) . Finally, to prove (i) we simply use the definitions of the obs a through k, obtaining, for all integers n ≥ 0, P n ≡ k I I n → a(b(h(j(Axy:I)))) Ω Ω I n → b(h(j(Axy:I))) n Ω I → h(j(Axy:I)) n 0 I 2 → j(Axy:I) n n I 2 by (v) 2 → (Axy:I) n n I,n → I,n 2 2 → <n > 115 Returning to the discussion of iteration statements, we can express the general for statement of ALGOL 60 in terms of the simple while loop treated above. For example, we can reformulate the statement for vi := e1 step e2 until e3 do S as begin vi := e1 while (vi - e3) × sign(e2) ≤ 0 repeat begin S; vi := e1 + e2 end end . The latter form can then be represented as an ob by employing the representations of compound and while statements. Omitting the details of derivation, we list below the SK representations for the three cases of for list elements, namely, arithmetic expression, (ALGOL 60) while element, and step-until element: {for vi := e do S}(v1,...,vn) ≡ Aφv1...vn: {S}φv1...vi-1{e}vi+1...vn . {for vi := e while b do S}(v1,...,vn) ≡ µx:Aφv1...vn:(Avi:{b}){e}({S}(xφ))φv1...vi-1{e}vi+1...vn ≡ Y(Axφv1...vn:(Avi:{b}){e}({S}(xφ))φv1...vi-1{e}vi+1...vn). {for vi := e1 step e2 until e3 do S}(v1,...,vn) ≡ Axφv1...vn: (Y(Axφv1...vn:{(vi-e3)×sign(e2) ≤ 0} ({S}(Av1...vn:xφv1...vi-1{vi+e2}vi+1...vn)) φv1...vn))φv1...vi-1{e1}vi+1...vn . 116 4.3 Jump Statements We regard the execution of the statement S ≡ goto L in a program as the substitution of the part of the program following L for the one following S. This viewpoint provides us with the representation of both labels and jump statements. A label is identified with the part of the program following it. To be accurate, the representation of a label L occurring in a program P is taken to be the routine (Remark 3.9-2(2)) of the program P’ obtained from P by deleting all the statements, but retaining the declarations, that appear above L. This representation can be obtained in a simpler manner by using the following inductive scheme: Let the label L occur in a block b whose declared variables are v1,...,vn . (1) If L is followed by statements S1,...,Sm, and a label M, in that order, all within b, then {L} ≡ {S1}({S2}(...({Sm}{M})...)) (2) If S1,S2,...,Sm are the statements following L to the end of b, then {L} ≡ {S1}({S2}(...({Sm}(Av1...vn:N))...)) , where N ≡ I, if b is the outermost block, else N is the representation of the program part following b, that is, of the (possibly imaginary) label immediately after the end of b. 117 According to the rules of ALGOL, the label to which a jump can be made must be in a block which is the same as, or outer to, the block containing the jump statement. It follows that (the list of variables constituting) the environment of a jump statement must contain the environment of the referred label as a final segment. Suppose (v1,...,vn) is the environment of the statement S ≡ goto L, and (vm,...,vn), where 1 ≤ m ≤ n, is the environment of L, and let φ represent as usual the program remainder of S. The execution of S causes the program to compute the function {L}(vm,...,vn) instead of φ(v1,...,vn). Hence, the representation of S can be taken to be the ob Aφv1...vn: {L}vm...vn , or the (n+1)-interconvertible ob Aφv1...vm-1: {L} . Thus, we choose: (3-1)Representation Rule. {goto L, where the environment of L is (vm,...,vn), 1≤m≤n}(v1,...,vn) ≡ Aφ:(Av1...vm-1:{L}) ≡ Km {L} . It is sometimes convenient, specially in connection with conditional statements, to write the right-hand side in the alternative forms: Aφv1...vn:{L}vm...vn , Aφv1...vn:(Av1...vm-1:{L})v1...vn . 118 Example. The representation of goto statements and labels is illustrated by means of a complete program. The program below has been derived from the program given in the previous example simply by expressing the while loop in terms of goto’s. As another application of the model, we prove the (input-output) equivalence of the two programs. As before, the representations of individual statements are shown on the same line as the statement, or on the last line for a multiple-line statement, and are designated identifying names. The obs common to the representation of both programs have the same names. The label M serves to illustrate the case (1) of label representations discussed above; it is otherwise superfluous. begin integer x,y; read x; a ≡ Aφxyoi:φiyo y := 0; b ≡ Aφxy:φx0 begin integer z; z := 0; L: c ≡ Aφzxy:φ0xy if z=y then goto N else goto M; M: d’≡ Aφzxy:(=zy)(Az:N)Mzxy y := y+2×z+l; e’≡ Aφzxy:φzx(+(+y(×2z))1) z := z+l; f’≡ Aφzxy:φ(+zl)xy goto L g’≡ Aφ:L end; h’≡ Aφ:c(d’(e’(f’(g’(Az:φ)))))Ω 119 N:write y j ≡ Aφxyo:φxy o,y end k’≡ Aφ:a(b(h’(j(Axy:φ))))ΩΩ {program} ≡ P ≡ k’II L ≡ d’M M ≡ e’(f’(g’(Az:N))) N ≡ j(Axy:I) We wish to prove that the above program and the program of the previous example produce the same output when executed with the same non-negative integer as the input data. That is, in terms of their representations, we wish to show that for all integers n ≥ 0, P n ↔ P’n . (i) Of course, this can be shown by using the previously obtained 2 result Pn → <n > in conjunction with a direct proof of the 2 fact that P’n → <n >. But we will prove the equivalence of the programs by verifying, in effect, that their differing parts do the same work when the programs are executed. differing parts are represented by the obs h and h’. These If we can show that for all integers n ≥ 0, h N n 0 ↔ h’N n 0 (ii) (where N ≡ j(Axy:I), defined in the present example), then (i) is demonstrated as follows. From the previous example, part (d), we know that for all n ≥ 0, 120 Pn → h(j(Axy:I))n 0 I ≡ hNn 0 I . But, using the definitions of the present example, we also have P’n ≡ k’IIn → a(b(h’(j(Axy:I))))ΩΩIn → b(h’(j(Axy:I)))nΩI → h’(j(Axy:I))n 0I ≡ h’Nn 0I . Hence, it follows from (ii) that Pn ↔ P’n . It remains to verify (ii). From (v) in the previous example, we have for all integers n ≥ 0, 2 h N n 0 → N n n . So (ii) would follow if we can also prove 2 h’ N n 0 → N n n . (iii) To outline the proof of (iii), we simply state the sequence of reduction relations leading to it. 2 N n n (1) L i n i 2 , if i = n , → L i+l n (i+1) 2 , if i ≠ n . (2) L 0 n 0 → L i n i2, for 0 ≤ i ≤ n . (3) L 0 n 0 → N n n2, for n ≥ 0 . (4) h’φ n 0 → N n n2 , for n ≥ 0 . The treatment of designational expressions and switches is omitted, except for an example which should suffice to indicate 121 how these may be represented as obs. In the schematic program below, b and c denote Boolean, and e and f, arithmetic expressions. It is assumed that the omitted statements indicated by ellipses do not contain any declarations. begin integer x; M: ... begin integer y; ... begin integer z; switch P := N, if b then P [e] else L, M; ... N: ... begin integer w; ... goto if c then N else P [f]; end w; end z; L: ... end y end The representations of the switch and goto statements in the above program are, respectively, 122 {P } ≡ <{N},{b}(Azyx:{P}(elem {e}3)zyx)(Az:{L}),Azy:{M}>, and Aφwzyx:{c}(Awzy:{M})(Aw:{P}(elem {f}3)). (Cf. Section 3.11.) Note that in the above two formulas 3, {e}, and {f} are to be natural number representations. CHAPTER 5 PROCEDURES 5.1 F-procedures We use the term F-procedure to denote a type procedure without any side effects. In particular, an F-procedure is a procedure in which (1) the procedure name is typed, (2) all parameters are called by value, (3) no global variables are modified, (4) no jumps are made outside the procedure body, (5) no procedures are used other than F-procedures. Because of the above restrictions, the representation of F-procedures is much simpler than that of general procedures. Since many procedures encountered in programs are truly F-procedures, it seems useful to deal with them as a special case. For the moment, let us consider only the F-procedures which do not involve global variables at all. For these, the environment of the declaration is immaterial. Let f be an F- procedure and p1,...,pn be its parameters. We wish to represent f in such a manner that for all expressions e1,...,en {f}{e1}...{en} → {f(e1,...,en)} . 123 (i) 124 Such a representation is accomplished as follows: We use a variable to denote the F-procedure value; that is, π all assignments to f are represented as if made to π. Further, we represent the statement S constituting the body of f by taking its environment to be (π,p1,...,pn). with an arbitrary value of Now, starting π, and the values ei of pi , the execution of S has the effect of assigning the value f(e1,...,en) to π, and certain values to pi which are irrelevant to the result; say, we have {S}φ π {e1}...{en} → φ {f(e1,...,en)}p1...pn . To obtain (i) from (ii), we may initialize π (ii) with Ω, and choose the ob Aφp1...pn:π for φ and {S}φπ for {f}. Thus we adopt the following (1-1) Representation Rule. {F-procedure f(p1,...,pn) with body S} ≡ {S}(π,p1,...,pn)(Aπp1...pn:π)Ω It should be pointed out that a label appearing in the body of an F-procedure is to be represented as the part of the F-procedure (not the program) that follows the label. Example. The following is an F-procedure; hence (1-1) is applicable. 125 integer procedure mod(x,y); value x,y; integer x,y; begin integer q; q := x+y; a ≡ Aφqπxy:φ(+xy)πxy mod := x-y×q b ≡ Aφqπxy:φq(-x(×yq)xy end q; c ≡ Aφ:a(b(Aq:φ))Ω mod ≡ c(Aπxy:π)Ω Example. Representation of the factorial function. integer procedure fact(n); value n; integer n; fact := if n = 0 then 1 else n × fact (n-1); As the body of this F-procedure consists of a single assignment statement, we have, by (3.5-1), {body} ≡ Aφπn:φ((=n0)1(×n(fact(-n1))))n . Hence, the representation of the F-procedure is given by the recursively defined ob fact ≡ {body}(Aπn:π)Ω → An:(=n0)1(×n(fact(-nl))). A non-recursive definition of the above ob is fact ≡ Y(Azn:(=n0)1(×n(z(-nl)))). Finally, it is easy to remove the restriction about global variables imposed earlier on functions: In case the global variable values are used (but not, of course, modified) in an F-procedure, we append the global variables to the actual 126 arguments as if they also were parameters in addition to the explicitly declared parameters of the F-procedure. This is illustrated below. Example. begin integer x,y; - integer procedure f(n); value n; integer n; f := n+x; a ≡ Aφπnxy:φ(+nx)nxy ... f ≡ a(Aπnxy:π)Ω begin integer z; x := f(y)+z; Aφzxy:φz(+(fyxy)z)y ... 5.2 Call-by-name, Side-effects In the previous section, we have described the SK representation of procedures subject to rather stringent conditions. We will now show how the representations can be extended to more general procedures, allowing call-by-name, the modification of global variables, and side effects. However, we limit ourselves here to considering the formal parameters of the type integer and label only. The extension of the model to include real and Boolean parameters is trivial. In ALGOL 60, a procedure call is intended to have the effect of an appropriately modified copy of the procedure body [27]. The modification in the case of call-by-name consists in replacing each instance of a called-by-name formal parameter by 127 the corresponding actual parameter. (It is understood that any name conflicts between the variables appearing in the actual parameter expressions and the local variables of the procedure are to be first removed by renaming the latter variables.) Instead of performing such symbolic substitution, however, which would require keeping procedures in text form at the execution time, most ALGOL compilers accomplish the same effect by treating formal parameter references in procedures as calls on special “parameter procedures” generated from actual parameters [30]. As a result, if an operation refers to a formal parameter during the execution of a procedure, then the procedure execution is suspended to evaluate the corresponding actual parameter in the environment of the procedure calling statement, and then the procedure execution is resumed using the thus-acquired value in the operation. Of course, depending upon the type and use of a parameter, the actual parameter evaluation may yield a value (e.g., an arithmetic or Boolean quantity when the formal parameter is an operand in an expression) or a name (e.g., the address of a variable when the formal parameter appears to the left of an assignment statement). Our SK interpretation is based on a similar idea. But we are able to avoid the notion of address, and work exclusively with values, by making use of a number of different “parameter procedures” for different operations performed with the same parameter; namely, the evaluation of actual parameter expressions, making assignments to the variables provided as 128 actual parameters, and jump to an actual label. 5.3. Integer Parameters In the absence of procedures we were able to express each statement in a program as a function which had for its arguments the variable φ, denoting the program remainder (that is, the part of the program following the statement), and the variables constituting the environment of the statement. Clearly the representation of a statement S in a procedure body would involve two sets of program remainders and environments - namely, one set for S itself and one for the statement, say T, that calls the procedure. The program remainder of T corresponds to the familiar “return” address or label for the procedure call. Now, any formal parameter instances in S give rise to actual parameter evaluations in the environment of T, but after the evaluation the control must eventually transfer back to S. Hence the representation of parameter evaluation also involves the two sets of environments and program remainders; but this time the program remainder of S serves as the return address. We will use the variable ρ to indicate the program remainder at the return point and φ, as usual, for the program remainder at the current point. We have so far represented, and will continue to represent, each program variable by a single indeterminate. The representation of an assignment statement may be 129 conceived as “binding” the indeterminate representing the variable appearing at the left-hand side to the representation of the right-hand expression. 1 In general, the indeterminates representing program variables are “bound” at any time to the current values of the corresponding program variables. With each called-by-value formal parameter we similarly need to associate a single indeterminate, bound to the current “value” of the parameter at any time. However, we need to carry more information with a called-by-name formal parameter; depending on the type and use of a parameter we shall associate a number of indeterminates with it. For each called-by-name formal parameter of type integer, we require three indeterminates best thought of as being bound, respectively, to the “value” associated with it and to the “parameter procedures” for evaluating it and making assignments to it. If p is an integer parameter, then these three indeterminates will be usually denoted by p, pε , and pα. label will be discussed later.) (The parameter of type The environment of a statement in a procedure body will contain the variables corresponding to all of the above-mentioned indeterminates; specifically, it will consist of the following in the given order: 1 The present descriptive use of “binding” and “bound” has no connection with the terms defined at the beginning of Section 2.3. 130 (a) variables local to the procedure, (b) ρ, the “return” variable, (c) variables representing the formal parameters, (d) variables global to the procedure. Next, let us turn to the procedure call. Associated with each called-by-name actual parameter p of type integer, and individual to each procedure call, is an ob that represents the “parameter procedure” for its evaluation. In case p is a program variable (rather than an expression), there is also another ob which represents the “parameter procedure” to effect the assignments to p called for in the procedure. These obs, referred to as “actual evaluation” and “actual assignment” operators, are denoted a and εp a α p , respectively, with further distinguishing marks added when more than one procedure call is involved. Last, let us consider the procedure declaration. Associated with each called-by-name formal parameter of type integer, and unique to each environment within the procedure body, are two obs which represent the calls on the “actual evaluation” and “actual assignment” parameter procedures mentioned above. For convenience, these obs are referred to as “formal evaluation” and “formal assignment” operators, and are f usually denoted ε p and f α p , where p is the formal parameter, with further distinguishing marks added if more than one environment is involved. If, in a statement in a procedure 131 body, a formal parameter appears as an operand of an expression, the statement will be represented as if preceded by a formal evaluation; likewise, if a formal parameter occurs at the left-hand side of an assignment statement, that statement will be represented as if immediately followed by a formal assignment. The above ideas will now be illustrated by means of a very simple example in which the declaration and the call of a procedure have the same environment. begin integer y; procedure P(x); integer x; x := x+2; y := 1; P(y) end The body of the above procedure consists of a single statement, and that statement needs to be both preceded by a formal evaluation and followed by a formal assignment. Thus, it is represented by the compound f f Aφ: ε x (a(α x φ)) ≡ b , say, where a is the representation of x := x+2 as an ordinary assignment statement. Since there are no local variables in the procedure, the environment of this latter statement consists of the following: ρ the “return” variable, xε the “parameter evaluation” variable, 132 xα the “parameter assignment” variable, x the “parameter” variable, and y the global variable . Thus we can write a ≡ Aφρxεxαxy: φρxεxα(+x2)y . Now, as the variable xε is bound to the actual evaluation operator, and the formal evaluation consists of just an application of this ob, we define f ε x to be Aφρxεxαxy: xερφxεxαxy , or more simply, Aφρxεxαx: xερφxεxαx . Note the interchange of φ and ρ above; this signifies that the program remainder at the return point of procedure call becomes the current program remainder during parameter evaluation, and vice versa. In a similar manner, we define f α x ≡ Aφρxεxαx: xαρφxεxαx . (In general, the global variables of the procedure need not appear in the formal evaluation and assignment operators.) The whole procedure may be represented by P ≡ b(Aρxεxαx:ρ) which displays the effect that once the procedure execution is over, (after the application of b), only the return variable is retained, and the other variables, namely, the ones connected with parameters, are deleted from the environment. Next, let us look at the procedure call. There is only 133 one call-by-name actual parameter of type integer in this a statement. So we need to define two obs ε x actual evaluation and assignment operators. and a α x , the These serve essentially as the fictitious assignment statements x:=y and y:=x (in the environment of the procedure call), respectively, and thus can be defined by a , a . ε x ≡ Aφρxεxαxy: ρφxεxαyy α x ≡ Aφρxεxαxy: ρφxεxαxx Again the interchange of φ and ρ is needed to represent the fact that after evaluating the actual parameter in the environment of the procedure calling statement, the control passes back to the procedure body. 2 The purpose of the procedure calling statement itself is three-fold: (a) to extend the environment from (y) to (xε,xα,x,y) (b) to initialize the added variables; that is, substitute a a ε x for xε, α x for xα, and, by convention, Ω for x. 2 It should not be difficult to see that coroutines can be represented by using the same idea, as follows: the “remainder” of each coroutine may be represented by a different variable. The coroutine calls are then representable by the obs which simply permute these variables to bring the remainder of the called coroutine in front. We will soon see how we can also account for the private variables of a coroutine by “covering” them when the control passes out of it and “uncovering” them on return. 134 (c) to apply P before the rest of the program; that is, substitute Pφ for φ. Consequently, the statement P(y) above may be represented by the ob a a (Axεxαx: (Aφy: Pφxεxαxy))ε x α x Ω , or, more simply, by a a Aφy: P φ ε x α x Ω y . Putting together the representations obtained piecemeal above, and adding the ones for the assignment and the block, we can now complete the representation of the program: Example. begin integer y; procedure P(x); integer x; f ≡ Aφρxεxαx: xερφxεxαx αx f ≡ Aφρxεxαx: xαρφxεxαx a ≡ Aφρxεxαxy: φρxεxα(+x2)y b ≡ Aφ: ε x (a(α x φ)) P ≡ b(Aρxεxαx:ρ) y := 1; c ≡ Aφy: φl P(y) d ≡ Aφy: Pφε x α x Ωy εx x := x+2; end f f a a εx a ≡ Aφρxεxαxy: ρφxεxαyy αx a ≡ Aφρxεxαxy: ρφxεxαxx e ≡ Aφ:c(d(Ay:φ))Ω {prog } ≡ eII 135 Next, let us consider the SK representation of type procedures in which a value is associated with the procedure identifier. In this case we will use an additional variable π to denote the procedure value in representing the statements of the procedure body. The representations are otherwise similar to those for the untyped procedures discussed above. A statement in which the function designator of a procedure is used as an operand of an expression will be represented as if it were compounded of two statements -- the first a procedure call to obtain the value of the procedure, and the second using that value in the expression. The representation of a type procedure is shown in the following example, which also illustrates the treatment of call-by-value in our present scheme of procedure representation. (Some explanation follows the program.) Example. begin integer u,v; integer procedure P(x,y); integer x,y; value y; f ε x ≡ Aφρπxεxαxy:xερφπxεxαxy f α x ≡ Aφρπxεxαx:xαρφπxεxαxy begin P := x-y; x := y a ≡ Aφρπxεxαxyuv:φρ(-xy)xεxαxyuv b ≡ Aφ:ε x (aφ) c ≡ Aφρπxεxαxyuv:φρπxεxαyyuv d ≡ Aφ:c(α x φ) f f 136 end compound e ≡ Aφ:b(dφ) end P; P ≡ e(Aρπxεxαxy:ρπ) u:=v:=3; f ≡ Aφuv:φ3 3 u:=P(v,u+1) + u; g ≡ Aφuv:PφΩε x α x Ω(+ul)uv end a a εx a ≡ Aφρπxεxαxyuv:ρφπxεxαvyuv αx a ≡ Aφρπxεxαxyuv:ρφπxεxαxyux h ≡ Aφπuv:φ(+πu)v k ≡ Aφ:g(hφ) m ≡ Aφ:f(k(Auv:φ))ΩΩ {prog } ≡ mII The environment of the statements in the procedure above consists of eight variables: the return variable ρ, the procedure value variable π, the three variables xε, xα, and x for the called-by-name parameter x, the single called-by-value parameter variable y, and finally the two global variables u and v. Of these, the four parameter variables are effectively discarded at the end of the procedure body execution by the component (Aρπxεxαxy:ρπ) of P above. The procedure call is represented as the compound of two statements f and g: f computes π, the procedure value, and g makes use of this in the assignment statement. In both previous examples, the environment of the procedure declaration and the procedure call are the same. In the general case, these environments may be different; this is so, for example, when a procedure call takes place in a block enclosed by the block that declares the procedure. When this 137 happens, there arises the problem of “covering” the local variables of the calling point whose scopes do not include the procedure declaration. Of course, the covering must be such that the variables may be “uncovered” on return to the calling point. Notice the contrast with jumps in which the variables that do not have valid declarations at the jump label are simply discarded permanently. Covering is also needed in specifying the formal evaluation and assignment operators for use with statements inside a block in a procedure body, since in this case, again, the variables local to the procedure body are invisible at the calling point. The following example shows a way of covering the nonoverlapping parts of the environment, in order to overcome the environment conflict problem. (See explanations below.) Example. begin integer x; procedure P(y);integer y; begin integer z; f ε y ≡ Aφzρyεyαy:ρyε(Aψ:ψφz)yεyαy f α y ≡ Aφzρyεyαy:ρyα(Aψ:ψφz)yεyαy z := y+3; a ≡ Aφzρyεyαyx:φ(+y3)ρyεyαyx f b ≡ Aφ:ε y (aφ) end block c ≡ Aφ:b(Az:φ)Ω 138 end P; P ≡ c(Aρyεyαy:ρI) begin integer u; a P(u+x); d ≡ Aφux:P(Aψ:ψφz)ε y ΩΩx a ... ε y ≡ Aφuρyεyαy:ρI(Aψ:ψφz)yεyα(+ux)x end end In representing the procedure call in the above example, (Aψ:ψφz) is passed as the return point argument instead of φ, thus covering u. The application of (Aψ:ψφz) to any ob has the effect of uncovering u and restoring the environment; e.g., in f ε y the application is made to yε, and in P, to I. Note that in the representation of the procedure call, namely, d, we have a used Ω for what would otherwise have been α y ; this is so, because no assignment can be made to the particular actual parameter in this case. The evaluation and assignment operators, both formal and actual, have been defined above slightly differently than in the two previous examples in which covering was not required. These two examples are worked out once again so as to make the treatment uniform, whether or not covering is needed in a particular case. 139 Example. begin integer y; procedure P(x); integer x; f ε x ≡ Aφρxεxαx:ρxε(Aψ:ψφ)xεxαx f α x ≡ Aφρxεxαx:ρxα(Aψ:ψφ)xεxαx x := x+2; a ≡ Aφρxεxαxy:φρxεxα(+x2)y b ≡ Aφ:ε x (a(α x φ)) P ≡ b(Aρxεxαx:ρI) y := 1; c ≡ Aφy:φl P(y) d ≡ Aφy:P(Aψ:ψφ)ε x α x Ωy f f a a a ε x ≡ Aφρxεxαxy:ρI(Aψ:ψφ)xεxαyy a α x ≡ Aφρxεxαxy:ρI(Aψ:ψφ)xεxαxx end e {prog } ≡ Aφ:c(d(Ay:φ))Ω ≡ eII Example. begin integer u,v; integer procedure P(x,y); integer x,y; value y; f ε x ≡ Aφρπxεxαxy:ρxε(Aψ:ψφ)πxεxαxy f α x ≡ Aφρπxεxαx:ρxα(Aψ:ψφ)πxεxαxy begin P := x-y; a ≡ Aφρπxεxαxyuv:φρ(-xy)xεxαxyuv f b ≡ Aφ:ε x (aφ) x := y c ≡ Aφρπxεxαxyuv:φρπxεxαyyuv f d ≡ Aφ:c(α x φ) 140 end compound e ≡ Aφ:b(dφ) end P; P ≡ e(Aρπxεxαxy:ρIπ) u := v := 3; f ≡ Aφuv:φ3 3 u := P(v,u+1)+u; g ≡ Aφuv:P(Aψ:ψφ)Ωε x α x Ω(+ul)uv a a a ε x ≡ Aφρπxεxαxyuv:ρI(Aψ:ψφ)πxεxαvyuv a α x ≡ Aφρπxεxαxyuv:ρI(Aψ:ψφ)πxεxαxyux h ≡ Aφπuv:φ(+πu)v k ≡ Aφ:g(hφ) end l ≡ Aφ:f(k(Auv:φ))ΩΩ {prog } ≡ lII For subscripted variables occurring as actual parameters, the actual evaluation and assignment operators are again chosen so as to represent the fictitious assignments between the formal and actual parameter variables. But now this involves the obs elem and replace introduced in the discussion of arrays (Section 3.11). We will simply illustrate the representation by means of an example. (The statements denoted by ellipses are assumed not to contain any declarations.) Example. begin integer n; ... begin integer array x [l:n]; procedure P(u,v); integer u,v; 141 begin ... end P; begin integer y; ... P(y,x [y]) end end end For the above program, the representation of the procedure calling statement P(y,x [y]) is the ob a a a a Aφyxn:P(Aψ:ψφy)ε u α u Ωε v α v Ωxn, where a ε u ≡ Aφyρuεuαuvεvαvxn:ρI(Aψ:ψφy)uεuαyvεvαvxn, a α u ≡ Aφyρuεuαuvεvαvxn:ρI(Aψ:ψφu)uεuαuvεvαvxn, a ε v ≡ Aφyρuεuαuvεvαvxn:ρI(Aψ:ψφy)uεuαuvεvα(x(elem yn)xn, a α v ≡ Aφyρuεuαuvεvαvxn:ρI(Aψ:ψφy)uεuαuvεvαv(x(replace ynv)n. A procedure body may contain a procedure call, possibly a recursive one, in which the formal parameters are used in actual parameter expressions. And the parameters of the nested call may themselves be called by name. The representation in such a case requires the covering of all the variables associated with the procedure body, including the local variables, the return variable, and the parameter variables. 142 This is illustrated below. Example. begin integer x; procedure P(y,n); integer y,n; value n; begin ... end P; procedure Q(z); integer z; begin integer w; P(z,x); ... end Q; ... end If the representation of the body of the procedure P is a, then the representation of P itself is P ≡ a(Aρyεyαyn:ρI) . The representation of the statement P(z,x) is f Aφ:ε z (bφ) , f where ε z is the formal evaluation operator for z in Q, and b represents the call on P, as follows: b ≡ Aφwρzεzαz x: P(Aψ:ψφwρzεzαz)ε a a y α y Ωx x , a ε y ≡ Aφwρzεzαz ρ1yεyαy x: ρ1I(Aψ:ψφwρzεzαz)yεyαz x , a α y ≡ Aφwρzεzαz ρ1yεyαy x: ρ1I(Aψ:ψφwρzεzαz)yεyαy x . 143 The next example illustrates the representation of a procedure calling statement in which an actual parameter itself consists of a call on a procedure. Example. begin integer x; procedure P(r,s); integer r,s; begin ... end P; procedure Q(t); integer t; begin ... end Q; begin integer y; ... P(x,Q(y)); end end Because the second actual parameter, Q(y), in the above procedure calling statement P(x,Q(y)) does not require an 3 assignment operator, the latter statement is represented by the ob a a a Aφyx:P(Aψ:ψφy)ε r α r Ωε s ΩΩx . The first actual parameter, x, poses no problem other than the covering of the variable y not visible to the procedure declaration of P; hence, we define a ε r ≡ Aφyρrεrαrsεsαsx:ρI(Aψ:ψφy)rεrαxsεsαsx , a α r ≡ Aφyρrεrαrsεsαsx:ρI(Aψ:ψφy)rεrαrsεsαsr . 3 As explained earlier, an assignment operator is required for those actual parameters which consist of a single program variable. 144 For the second actual parameter, Q(y), things are slightly more complex. (Note, however, that only an evaluation operator is needed in this case; the assignment operator is undefined.) First, we have to provide for a call on Q -- which requires covering all the variables associated with the call on P -- with the following actual evaluation and assignment operators: a ε t ≡ Aφyρ rεrαr sεsαs ρ1πtεtαtx:ρ1I(Aψ:ψφyρrεrαrsεsαs)πtεtαyx a α t ≡ Aφyρ rεrαr sεsαs ρ1πtεtαtx:ρ1I(Aψ:ψφtρrεrαrsεsαs)πtεtαtx a Now, ε s is defined in terms of a call on Q, followed by an assignment of the resulting value to s, as follows: a a a ≡ Aφyρ rεrαr sεsαs x:Q(Aψ:ψφyρrεrαrsεsαs)Ωε tα tΩx , b ≡ Aφyρ rεrαr sεsαs πx:ρI(Aψ:ψφy)rεrαrsεsαπx , a ε s ≡ Aφ:a(bφ) . 145 5.4 Label parameters The representation of label parameters is actually much simpler than of the integer variety. The reason is that two different operations, evaluation and assignment, are possible with the latter type; in addition, the value of the parameter at any time has to be carried also along within the representation. In the case of a label parameter, the only possible actual operation is a jump to it. Thus, with each formal label parameter, p, we need to associate only one variable, denoted by pγ, which is to be bound to the operator for effecting the actual goto operation. (The variable pγ is, of course, an element of the environment of the procedure body.) Next, associated with each actual label parameter, and individual to each procedure call, is an ob that represents the parameter procedure to effect the jump to the actual label. For a parameter p, this “actual goto” operator is denoted by a γ p , with further distinguishing marks added when more than one procedure call is involved. Last, associated with each formal label parameter, and unique to each environment within the procedure body, is an ob, the “formal goto” operator, that represents a call on the actual parameter procedure, that is, an application of the actual goto operator; the formal goto f operator for the parameter p is denoted γ p , again with further distinguishing marks added if more than one environment is involved. For anyone who has followed the previous treatment of jumps 146 (Section 4.3) and procedures with integer parameters (Section 5.3), the example below should suffice to explain how to represent label parameters. Example begin integer q; procedure R(v); label v; goto v; f a ≡ γ v ≡ Aφρvγ: ρvγ(Aψ:ψφ)vγ R ≡ a(Aρvγ:ρI) begin integer r; procedure P(x,z); integer x; label z; f ε x ≡ Aφρxεxαxzγ:ρxε(Aψ:ψφ)xεxαxzγ f α x ≡ Aφρxεxαxzγ:ρxα(Aψ:ψφ)xεxαxzγ f γ z ≡ Aφρxεxαxzγ:ρzγ(Aψ:ψφ)xεxαxzγ R(z); a b ≡ Aφρxεxαxzγrq:R(Aψ:ψφρxεxαxzγr)γ v q a f γ v ≡ Aφρxεxαxzγrρ1vγq:γ z φρxεxαxzγrq begin integer s,t; P(t,L) a a a c ≡ Aφstrq:P(Aψ:ψφρst)ε x α x Ωγ z rq a ε x ≡ Aφstρxεxαxzγrq: ρI(Aψ:ψφst)xεxαtzγrq a αx a ≡ Aφstρxεxαxzγrq:ρI(Aψ:ψφsx)xεxαxzγrq γ z ≡ Aφstρxεxαxzγrq: L r q end; L: ... end end CHAPTER 6 CONCLUSION In this dissertation, we have presented a combinatory logic (or, equivalently, lambda-calculus) model of programming languages. Since a number of programming language models based on the same calculi have already appeared in the literature [5,11,16,17,18,28,31,36], a comparison of our model with others is in order. 1. Our model does not introduce any imperative or otherwise foreign notions to the lambda-calculus. This is in contrast to Landin [17], in which the imperative features of programming languages are accounted for by ad hoc extensions of the lambda-calculus. We find that the calculus, in its purity, suffices as a natural model of programming languages. By not making any additions to the calculus, we have the guarantee that all its properties, in particular, the consistency and the Church-Rosser property, are valid in our model, For example, even when a program requires a fixed order of execution, the normal form obtained by evaluating the program representation in any order, whatsoever, represents the program result correctly. 2. In our model, programs are translated into lambdaexpressions, not interpreted by a lambda-calculus interpreter (Reynolds [31]). Thus, programming semantics is completely reduced to the lambda-calculus semantics, but without 147 148 commitment to any particular view of the latter. Also, all lambda-expression transformations are applicable to program representations. 3. We model assignments by the substitution operation of the lambda-calculus. Consequently, the notions of memory, address, and fetch and store operations do not enter our model in any explicit manner (Stratchey [36], Reynolds [31]). 4. We represent high-level programming language constructs directly, not in terms of the representations of the machine level operations (Orgass-Fitch [28]) of the compiled code. 5. language. Our model potentially spans the full ALGOL 60 It is also applicable to a number of other advanced programming features, such as collateral statements, the use of labels and procedures as assignable values, coroutines, etc. 6. As a matter of opinion, it seems that our representations are much simpler and clearer than the ones given in other models. We have described the model informally, and only for a representative set of programming language constructs. But we have provided enough motivating details and illustrations to, hopefully, convey the method and suggest its extension to other programming features. Our explanations, we believe, are quite adequate for the detailed construction of an effective procedure, say, in the form of a compiler, to 149 translate ALGOL 60 programs into lambda-expressions. An immediate application of our model is in a functional (as opposed to computational) semantic definition of high-level programming languages, as the combinatory interpretations of the individual programming constructs can themselves be taken as the semantic specification of the constructs. Of more interest, however, is the potential of the present model in studying the properties of programs -- such as, convergence, correctness, and equivalence -- and in performing useful program transformations -- such as program simplification (source code level) and optimization (compiled code level). Since we describe a program as a lambda-expression or a combinatory object, the above-described applications essentially reduce to transformations within the respective calculi. The possibilities of some of these applications have been indicated by examples. In the case of loop-free programs, these applications most often involve straightforward lambdacalculus reduction. For the programs containing loops, our proofs of correctness and equivalence are rather ad hoc; the development of systematic methods to deal with these applications warrants further research. REFERENCES 1. Abdali, S. K., A simple lambda-calculus model of programming languages, AEC R & D Report cOO-3077-28, Courant Inst. Math. Sci., New York Univ. (July 1973). 2. deBakker, J. W., Formal Definition of Programming Languages, Mathematisch Centrum, Amsterdam (1967). 3. ______________, Recursive Procedures, Mathematical Center, Amsterdam (1972). 4. Böhm, C., The CUCH as a formal and description language, in Formal Language Description Languages ed. Steel, T. B.)., North-Holland, Amsterdam (1966), 179—197. 5. Burstall, R. M., Semantics of assignment, in Machine Intelligence 2 (ed. Dale, E., and Michie, D.), Oliver and Boyd, Edinburgh (1967), 3-20. 6. _______________, Formal description of program structure and semantics in first-order logic, in Machine Intelligence 5 (ed. Meltzer, B., and Michie, D.), Edinburgh Univ. Press, Edinburgh (1970), 79-98. 7. Cadiou, J. M. and Manna, Z., Recursive definition of partial functions and their computations, SIGPLAN Notices 7, 1 (Jan. 1972), 58—65. 8. Church, A., The Calculi of Lambda-Conversion, Princeton Univ. Press, Princeton, N. J. (1941). 150 151 9. Curry, H. B., and Feys, R., Combinatory Logic, Vol. I, North-Holland, Amsterdam (1956). 10. Floyd, R. W., Assigning meanings to programs, Proc. Symp. in Appl. Math., Amer. Math. Soc. 19 (1967), 19—32. 11. Henderson, P., Derived semantics for programming languages, Comm. ACM 15, 11 (Nov. 1972), 967—973. 12. Hoare, C. A. R., An axiomatic basis for computer programming, Comm. ACM 12, 10 (Oct. 1969), 576—580,583. 13. Igarashi, S., On the equivalence of programs represented by ALGOL-like statements, Report of Computer Center, Univ. of Tokyo 1, 1 (1968), 103-118. 14. Kleene, S. C., Introduction to Metamathematics, van Nostrand, Princeton, N. J. (1950). 15. Knuth, D. E., Examples of formal semantics, in Symp. on Semantics of Algorithmic Languages (ed. Engeler, E.), Springer-Verlag (1971), 212—235. 16. Landin, P. J., The mechanical evaluation of expressions, Computer J. 6, 4 (Jan. 1964), 308—322. 17. _______________, A correspondence between ALGOL 60 and Church’s lambda-notation, Comm. ACM 8, 2-3 (Feb., Mar. 1965), 89—101, 158—165. 18. Ledgard, H. F., A model of type-checking, Comm. ACM 15, 11 (Nov. 1972) 956—966. 152 19. Lee, J. A. N., Computer Semantics, van Nostrand Reinhold, New York (1972). 20. London, R. L., The current state of proving programs correct, Proc. ACM Annual Conf. (Aug. 1972), 39-46. 21. Lucas, P., Lauer, P., and Stigleitner, H., Method and notation for the formal definition of programming languages, Report TR 25.087, IBM Laboratory, Vienna (1968). 22. Manna, Z., and Vuillemin, J., Fixpoint approach to the theory of computation, Comm. ACM 15, 7 (July 1972), 528— 536. 23. McCarthy, J., A basis for a mathematical theory of programming, in Computer Programming and Formal Systems (ed. Braffort, P., and Hirshberg, D.), North-Holland, Amsterdam, (1963), 33—70. 24. McGowan, C., Correctness results for lambda-calculus interpreters, Ph.D. thesis, Comp. Sci. Dept., Cornell Univ. (1971). 25. Milner, R., Implementation and application of Scott’s logic for computable functions, SIGPLAN Notices 7, 1 (Jan. 1972), 1—6. 26. Morris, J. H., Lambda-calculus models of programming languages, Ph.D. thesis, Project MAC, MIT, MAC-TR-57 (Dec. 1968). 27. Naur, P. et al., Revised report on the algorithmic language ALGOL 60, Comm. ACM 6, 1 (Jan. 1963), 1-17. 153 28. Orgass, R. J., and Fitch, F. B, A theory of programming languages, Studium Generale 22 (1969), 113—136. 29. Petznick, G. W., Combinatory programming, Ph.D. thesis, Comp. Sci. Dept., Univ. of Wisconsin, Madison (1970). 30, Randell, B., and Russel, L. J., ALGOL 60 Implementation, Academic Press, New York (1964). 31. Reynolds, J. C., Definitional interpreters for higherorder programming languages, Proc. ACM Annual Conf. (Aug. 1972), 717—740. 32. Rosen, B. K., Tree-manipulating systems and Church-Rosser theorems, J. ACM 20, 1 (Jan. 1973), 160—187. 33. Rosser, 3. B., Deux Esquisses de Logigue, GauthiersVillars, Paris (1955). 34. Schönfinkel, M., Über die Bausteine der Mathematischen Logik, Mathematische Annallen 92 (1924), 305-316. 35. Scott, D., Lattice theory, data types, and semantics, in Formal Semantics of Programming Languages (ed. Rustin, R.), Prentice-Hall, New Jersey (1972), 65—106. 36. Stratchey, C., Towards a formal semantics, in Formal Language Description Languages (ed. Steel, T.B.), NorthHolland, Amsterdam (1966), 198-220. 154 37. Wegner, P., Programming Languages, Information Structures, and Machine Organization, McGraw-Hill, New York (1968). 38. van Wijngaarden, et al., Report on the algorithmic language ALGOL 68, Numerische Math. 14 (1969), 79—218.