LIBRARY OF THE UNIVERSITY OF ILLINOIS AT URBANA-CHAMPAIGN 510.84 COjD. ^ Digitized by the Internet Archive in 2013 http://archive.org/details/conceptoftimeinp560adam If" UIUCDCS-R-T3-560 /%d^C>h THE CONCEPT OF TIME IN PROGRAMMING LANGUAGES by Edward NeufVille Adams III January, 1973 THE UBRA RYOFTHE MM* < lb 1 3 QlS The person charging this material is re- sponsible for its return to the library from which it was withdrawn on or before the Latest Date stamped below. Theft, mutilation, and underlining of books are reasons for disciplinary action and may result in dismissal from the University. UNIVERSITY OF ILLINOIS LIBRARY AT URBANA-CHAMPAIGN C UIUCDCS-R-73-560 THE CONCEPT OF TIME IN PROGRAMMING LANGUAGES Edward Neufville Adams III January, 1973 Department of Computer Science University of Illinois at Urb ana -Champaign Urbana, Illinois This paper was supported by the Department of Computer Science for the degree of Master of Science in Computer Science. Ill TABLE OF CONTENTS Page !• INTRODUCTION* 1 2. FEATURES OF TIME 2 2 . 1 Natural language features of time 2 2 . 2 Time in programming languages 3 3. PURPOSE OF TIME- 4 3.1 Utility ^ 3.2 Pragmatics 10 3*3 What should go in a programming language ? 12 4 . FEATURES IMPLEMENTED -^ 4.1 Syntax j\± 4.2 Semantics 15 4.2.1 Human semantics 15 4.2.2 Computer semantics 19 4.2.3 Conflicts 20 4.2.4 Questions of implementation 23 4.2.5 Summary of semantics 24 5. IMPLEMENTATION 2 6 5.1 Overall nature of the preprocessor 27 5.2 Details of implementation 2Q 6. EXPERIENCES IN USING THE LANGUAGE 32 IV Page 6.1 A program using the future imperative 32 6.1.1 Benefits of the language 36 6.2 An example of the past conditional 37 6.2.1 A basic difficulty exposed 38 6.2.2 The example program 38 6.2.3 Advantages of time expressions 1|.0 7. SUMMARY AND FUTURE PROPOSALS. k2 1. INTRODUCTION There has always been an interest in utilizing parts of natural language in programming languages with a view toward combining the superior learnability, usability, and readability of the former with the precision and power of the latter. At the same time, there is a trend in programming language toward simple means of expressing ever more complicated forms of sequencing of relatively simple calculations. Since natural language has been the medium of communication between people engaged in the planning and execution of giant, complex projects since prehistoric times, some of its features are probably well adapted to description of complex tasks. This paper discusses the general semantic feature of time, what parts of time have been implemented in programming languages, what parts could additionally be implemented, and the observed results of one such implementation. 2. FEATUKES OF TIME 2.1 Natural language features of time Several features of time (as observed in verbs and verb phrases) have to do with geometrical properties of the verbs and instances of the verbs. One of the most general distinctions recognized by linguists is between punctual (discrete) events and durative (continuous) conditions or states. There is a continuum of possible activities between the exploding of a firecracker and the building of a pyramid. Speakers assume that no activity could start or end in the period of time allotted to a small explosion, whereas many activities are assumed to have taken place between the beginning and the end of building a pyramid. Another feature of this sort is the fuzziness of the boundaries of some activities, as opposed to the completion or distinctness characteristic of some other activities. That is, there is a continuous function assigning to each time a probability that the activity is happening at that time. A third attribute, relating these two, is the notion of frequency . Sometimes an activity can be performed repeatedly, in rhythm, habitually. If such an activity has fuzzy boundaries, however, one could as easily construe the long period containing repeated fuzzy discrete instances as one continuous instance. A dimension of time relations among activities is order, generally expressed by tense . Some instances of different activities or the same activity may be thought of as occurring in a specified order; some might be thought of as occurring in some order, where the particular order is unspecified; and some might be thought of as occurring at the very same time. Another interesting aspect of order is that order of sentences in a text is not necessarily related to the order of the described events. Features of time not so obvious in the verb phrases themselves, but as important, are location and quantification . That is, when one set of events bears a fairly complicated relationship to another, it is frequently necessary to locate one with respect to another directly, by saying that one event occurred at some time. Quantification is the process of saying that an event occurred at some time, whose value is a variable defined on some range of times. Thus, in natural languages, instances of activities are all tagged, either explicitly or implicitly, with attributes of duration, fuzziness, frequency, and parameters locating these instances with respect to instances of activities on the time line. A text in natural language describes a complex activity in terms of its simpler components and their relative order and time -inter relation ships. 2.2 Time in programming languages What sorts of semantic aspects of time are there in computer languages? Statements to be executed are considered as punctual events, some statements are executed repeatedly through loops, and activities are assumed to happen in a linear order, although there is a semblance of simultaneity in ALGOL 68 and PL/ I. A further property of computer language is that, except for ON statements in PL/l, the execution image of a sequence of statements is in the same order as the sequence itself. 3. PURPOSE OF TIME 3.1 Utility It is not immediately obvious what use these features of natural language serve, or how they would help programming languages. So it is not clear how useful it would he to be able to distinguish between routines of types fuzzy or distinct, or between durative and punctual, or between frequent and one-shot . In natural language, a non-time dimension seems to be tied-in with frequency. Some verbs allow only a singular object, while some allow plural objects, whose elements are affected in parallel. In this sense, an ALGOL loop around a multiplication is a frequent verb operating on a singular object, whereas an APL multiplication is a one-shot verb operating on a plural object in parallel . There is also a tie-in between the continuum and frequency dimensions, in that in many languages, a similar construction is often used to express either a one-shot durative or a frequent punctual activity. These interrelated distinctions are probably most useful in providing redundancy for error-correcting. Thus although the two multiplication routines above could do the same thing, the APL multiplication, since it operates in parallel, can not contaminate one element's multiplication with the results of another. This lack of side-effects is guaranteed, both to the programmers and to a compiler, just because the APL multiply is a parallel plural verb. At first glance, the purposes of tense seem more clear. Yet, when first considered for inclusion in computer language, they seem to have no purpose. After all, what place does the ability to say (Nl) have in a programming language? (Nl) "He was mowing his lawn on Saturday." But the seeming uselessness of this statement is due more to its declara- tiveness than to its tense. While description does occur in programs, it occurs only as "declarations" and "specifications," which are all one-shot durative statements covering all of time (at least during invocation of their block). Otherwise the norm of statements is the imperative. If one were to imbed a past tense statement in a conditional imperative, suddenly the descriptive nature becomes useful. Notice statement (N2) does not relate to whether he is mowing his lawn today. (N2) "If he was mowing his lawn on Saturday, then don't buy any mulch until you see if you can get some clippings from him." Programmers rarely recognize that this type of statement is useful in a language, because one of their prime training objectives has been to try to find another condition which implies the past condition and can be tested now, such as condition (N3). (N3) "if he has a pile of grass clippings in his garbage cans" Sometimes this improves a program, but at other times it takes a lot of ingenuity or even requires outright brute force methods. So a programmer might have to include extra statements in the program. One would go in the 6 part of the program dealing with Saturday and the person who might have mowed (Nk), while another would have to go in the present part of the program (N5). (Nif) "If you are mowing your lawn, put a flag saying 'I have grass' in your front yard." (N5) "If he has an I-have-grass flag in his front yard, go get some rather than buy mulch at the store." This latter approach makes the program harder to read, because of the inclusion of extraneous nouns (the I-have-grass flag) and the statements setting and checking the flag. However, something is needed to fulfill the function of the flag, so that the program will sense the appropriate condition. It is part of the programmer's art to replace some conditions with other conditions which are more easily accessible and, because of the particular situations involved, imply the more difficult conditions. But a program written this way is more vulnerable to minor modifications in the environment than is a program written using the conditions that are intended to be relevant. The past tense in this conditional allows both desirable features. It is immediately clear what the tested condition really is, and yet the program is not cluttered with meaningless flags. So, if the past tense is useful, what use is the future tense? A meaningful implementation of conditionally future clauses is imaginable. Sentence (N6) (N6) "If the weather is going to be clear today, mow the lawn." 7 can be compiled by copying the environment into a stack, performing the operation "mow" and. all other operations called for before the weather for the day is ascertained, and then, if the weather turns out not clear, unstacking the entire environment, and popping back to the original point in time, choosing the other branch. This is probably not practicable, and it does not correspond to the behavior of natural language users. People using such clauses do what programmers always do: they try to find some other, more accessible condition implying the desired condition. Thus the gardener will look for certain patterns of light and dark in the sky, or listen for certain patterns of noise coming out of the weathermen. Translations of future conditions into implying present conditions cannot generally be done automatically except possibly by an automatic theorem prover, which is an awful investment to put into a compiler. Furthermore, it is hard for anyone to understand, in general, at what time it can be first said that some particular event did not happen. Yet this facility is needed in the compiler trying the backtracking strategy. It is easy to see how to compile the code for preserving the stack and for popping it later, but how does the compiler know where to put the check that asks "did it not rain?"? Future time is probably not practicable in conditional clauses. However, it is a typical feature of imperatives across the world. Since imperatives are the meat of a programming language, it should be easy to provide for a statement that is to be done, not at the moment of utterance, but at some future time. Recognition of this fact brings us to the question of "how do you denote an instant of time?", which is necessary both for future imperatives and past conditionals. 8 In natural languages we have words like Saturday, which are assumed to be shortened forms (via context-dependent transformations) of absolute numerically denoted time expressions such as Saturday, 1 July 1972 A.D. But it is the very nature of computer programs that for the most part, the output should be independent of the absolute time in which they were run. Therefore, true absolute times are rarely useful in programming languages. There is another type of time expression in natural language, the relative clause. This is useful for relating events in time by logical rather than numerical constraints. The form of a time -denotation is very simple; it is just a condition. It is more simply stated in the extension of predicate calculus that deals with tense and relations of time: an area of time is that area in which some predicate is true. Thus all times are mapped into predicates, and all predicates can be mapped into times (some of which are null). An example of this, statement (Wj), (Wj) "if it rained while he was mowing his lawn, don't borrow his lawnmower; it will be all gunked up with grass." demonstrates a condition ("it rained"), a time ("while he was mowing his lawn"), and the consequent of the conditional statement. Similarly such clauses are used in the future with imperatives. Statement (U8) (N8) "Mow the lawn whenever the grass gets 8 inches high." demonstrates the time ("when the grass gets 8 inches high") and the imperative to be performed then, not now ("mow the lawn"). So this digression on the denotation of instants (or areas) of time shows a possible computer meaning 9 of the imperative -in- the -future. The statement "When P is true, do S" means that S is a sequence of instructions which is not to be done right now, but rather is to be done as soon as P becomes true. The notion of denoting a piece of time is useful in another way than merely in conditionals or imperatives. An individual in the universe we live in, as opposed to the universe described in a programming language, grows or decays or changes in time, yet is still identifiable as that individual. When talking about an individual, a person refers to it not only as it is at the moment of speech, but also at other moments, both in the past and in the future. People refer to past values of attributes of the individual, in comparison to present values. The speaker of statement (N9), (N9) "If your grass gets higher than mine was when I mowed it, mow it." enraged about his neighbor's disrespect for property values, urges him to compare two lengths of grass: his own at the present, and the speaker's at a previous time. People are expected to remember past values of attributes, and they expect other people to do the same. When people are introduced to programming, they almost immediately find this block: they can not express the notion of a past value. What's more, since nobody tells them that past values are not remembered, they are constantly trying to write as if the computer could act on the past value. As objects, times can be quantified. There is a distinction in logic between existential quantification ("there is a time when...) and universal quantification ("for all (any) times when...). This difference 10 is exactly that between the principal meanings of "when" and "whenever". It is shown in a comparison of sentences (N10) and (NlOa). (N10) "When the grass gets higher than 8 inches, mow it." (NlOa) "Whenever the grass gets higher than 8 inches, mow it." The latter is a statement that is in force forever after its utternace, whereas the former is only in force until its condition is first satisfied, after which time it is disabled. This distinction is valid both in the future and in the past, as sentences (Nil) and (N12) show. (Nil) "if you got hay fever when you mowed the lawn, check it for ragweed." (N12) "If you got hay fever whenever you mowed the lawn, get someone else to mow the lawn." Sentence (Nil) expresses concern over the one-shot appearance of an allergen (ragweed), while sentence (N12) expresses recognition of the frequent appearance of an allergen (probably grass). J.2 Pragmatics The pragmatic properties of logical time denotation are worth describing. Imagine a program as being described by a lattice of possible states of the program, so that a statement corresponds to a node or a set of nodes. A statement can, by denoting some time, check for some condition that may have held in any line leading into this node. Or it can cause some other statement to happen in whichever line leading out of this node the program trajectory happens to follow. In either case, by having time-denotations, the programmer can collapse into one statement the 11 repetitive statements testing and performing various tasks. Further, this statement can he placed in the appropriate, logical place for the statement to occur. This advantage of the timed statement is not merely a luxurious convenience. In cases requiring this sort of structure anyway, if the program explicitly contains all the checks and tests, a great deal of uninformative material proliferates, masking what the program is really trying to say. A reader seeing a check for a flag does not know where it is set, or what it denotes „ A reader seeing a set for a flag does not know where it is checked, or what it entails. In one way, this situation is like the problem with GOTOs and labels. Although you may think you know all the GOTOs belonging to a given label, or vice versa, you can never be sure. Once the IF-THEN-ELSE structure of ALGOL and later languages came along, however, most of these labels, with their corresponding GOTOs, disappeared, because the programmer knew the flow to be more specifically structured. This allowed the reader to know that the flow was much more structured. Similarly, then, by seeing a time clause, a reader knows what sort of condition is relevant, rather than merely knowing the name of a meaningless variable. The pragmatic properties of quantification are also nice. In a procedural language, each statement carries a hidden condition meaning, roughly, "if execution is here, do...". The presence of quantification makes it possible to throw away that condition and make some statements applicable at any execution time. This can actually add security to a program. The future imperative with "time" B makes a statement to be performed at all times that its condition (B) is true. Since this statement can be an arithmetic statement of the form V = E, we can see a situation 12 where the = sign actually can mean both "assignment" and "is equal". That is, by issuing an imperative whose effect is to set V = E, one says in effect, "Whenever B is true, I know V is equal to E". This is a powerful statement, which can be used to make it easier to prove propositions about programs . 3.3 What should go in a programming language? It is quite hard to imagine which features of time should be imported into programming languages. It is even harder to imagine what interaction would be caused by several different features at once. So the easiest way to study the area would be to try one feature at a time. The mini-language approach Is best for implementing, but not instructive to use. Another approach might be to try to put everything all at once into a language. Unfortunately, time and everything else in natural language are somewhat intertwined, so that it is hard at present to say "these are pieces of the time apparatus" without bringing along some fragment of some other system of the language. A third approach, which this paper describes, is to implement a small number of features in a full language. Possible mini -language topics which should be studied are: denotation of times ("remember the time when...", "yesterday", "last time A was greater than 5"), evaluation of expressions in times other than present ("if this dog isn't bigger than your great-grandfather's dog, I'll eat my hat"), quantified times ("there was a time when...", "during all times when..."), simultaneity with guaranteed lack of side-effects (so that FOR 1=1 STEP 1 UNTIL N DO A(I+1):=A(I) might really do what you want it to), testing status of other activities ("if this activity is happening right now", "will this activity happen after now"), reference to future or past time with respect 13 to a loop (future or past occurrences in this iteration, occurrence in future or past iterations, occurences outside the loop "before or after this invocation of the looping), more specific sorts of time denotation (before, after, since, while). Although the underlying notion of time expressions has received essentially no attention in the computer science literature, some of the features I have described here have appeared in some forms. The most relevant is the DEBUG feature of FORTRAN, which parallels similar features in some ALGOL implementations and in PL/ I. This is a feature whereby the programmer can associate a section of code with a triggering variable. This is implicit in the CHECK condition of the PL/I ON-statement, which otherwise restricts itself almost entirely to simple systems interrupts. This feature is the sort of detail that underlies denotation of times. In fact, such a feature is assumed as an intermediate language in the language described later in this paper. A critical difference between the ON CHECK condition statement and a statement using time expressions is that the latter makes clear the relevant conditions. Morgan has evidently produced a language in which an ON condition can be any Boolean expression, although he does not seem to have related it to the general notion of time expressions. The simulation languages seem to have more advanced thoughts in these regards. Some of them have a WAIT UNTIL , which goes to sleep until some other routine causes the Boolean expression to become true. They have also developed some of the ideas necessary for recognizing and implementing several kinds of parallelism in time, although once again, they have not looked at the problem from the point of view of natural language, or natural language user . Ik k. FEATURES IMPLEMENTED Of all the possible candidates, I chose (a) conditions evaluated in past time, (b) imperatives performed in future time, and (c) quantifica- tion of times. These headings must be understood in regard to the default values of other features. The past and future times used are only logically computed times, not numerically computed. No attempt is made here to provide for various kinds of parallelism in time. Furthermore, although both existential and universal quantifiers can be meaningful in both present and past times, here I merely implemented existential past and universal future. it-.l Syntax The syntax chosen is the following slight modification to IBM ALGOL 60 : : := IF [WHEN ] THEN : := [WHENEVER DO] (The DO does not imply a loop, as it does in FORTRAN and ALGOL, but merely separates the time from the consequent.) An interesting thing about this syntax is that it uses no marker specific in function for tense denotation. Past time is always and only denoted by a time-clause (either a WHEN-clause or a WHENEVER -clause) in the condition of an IF-clause. Similarly the future time is always and <; 15 only denoted by the existence of a time-clause in an imperative. This is caused by the twin impracticalities of a future conditional and a past imperative. As languages become less procedural, I believe there will be more declarative statements in programming languages, at which time there will need to be specific tense markers for specifying descriptions of past, present and future situations. In this particular case, however, we find that past time is the inclusion of a time-clause in a condition (in any value at all, although not in this implementation), and future time is the inclusion of a time-clause in an imperative. This provides an interesting counterpoint to the modern argument that in programming language, there is no essential difference between a statement and a value: the difference is exactly the difference between future and past. To extend this syntax to handle the universally quantified past and the existentially quantified future, we need merely allow the choice of WHEN (existential) versus WHENEVER (universal) in both rules. For the sake of completeness, all combinations are discussed in what follows. k.2 Semantics Since a programming language is not merely a medium of communication from man to machine, but also between men, it is important to recognize the difference between the semantics -for-computer and the semantics-for- human. There should be an isomorphism between the two systems, but quite often there is not. Language designers self -righteously condemn the humans for the discrepancy, but I feel (with users) that designers, whose products do not conform to natural feelings of users, are at fault. 4.2.1 Human semantics As stated above, a Boolean condition can be mapped into an area of time, using those sections of the time line during which the condition was 16 true. If the area of time corresponding to the truth of the condition B2 happens to consist of one unbroken segment, then statement (Nlj) is unambiguous, and can be translated by sentence (NIJa). (N13) "IF Bl WHEN B2 THEN S" (NIJa) "If Bl was true when B2 was true, then do S." If the area of time corresponding to the truth of B2 is broken up into several non-contiguous segments, then the statement is generally ambiguous in natural languages. However, it can easily be made unambiguous by additional conditions, until the area consists of one unbroken segment. A typical feature added from context by natural language speakers is "the nearest instance" of B2. That is, in the absence of other contextual or explicit clues, we can translate statement (U13) as (N13b). (N13b) "If Bl was true the last time that B2 was true, then do S." (NllO "WHEW B3 DO S" (NlVb) "The next time B3 is true, do S." Similarly, sentence (KLk) can be unambiguously translated as (OTJ+b). So far I have assumed that the instance of B2 is quite short in relation to the length of Bl. Because the fact of duration or punctuality is still in the language, even though we have not attempted to implement control over it, we have a problem when B2 is a durative condition. There is no problem if the instance of B2 is completely engulfed in an instance of Bl. Then, If we look at the value of Bl when B2 was true, we find Bl had just one value. But if the instance of B2 overlaps an instance of Bl, then we could reasonably say that when B2 was true, Bl was true, or Bl was false, or even Bl was both true and false. So we are faced with the problem of IT defining, for human use, the meaning of statement (15) when instances of Bl and B2 overlap. Looking to natural language for help, we find that some occurrences of this phenomenon are treated by referring explicitly to the beginning or ending of an activity (through "inchoative" or "terminal" aspect markers on the verb); that some occurrences are treated by claiming that a given verb is punctual, so that its event cannot overlap another instance of something; and finally, that people are willing and able to claim that something does not make sense, or is vague. Although we are not implementing aspects such as punctual or inchoative, it is important to recognize that the normal situation finds a human attempting to construe the situation as having meaning. Thus we will try to give statement (N13) a consistent meaning rather than consigning it to the nether region of "undefined", as do so many other designers. We will arbitrarily assume that a time-clause refers to the beginning of its instance. Thus the meaning of statement (N13) is ultimately translated as (N13c), (N13c) "If Bl was true the last time B2 started to be true, do S." and the meaning of statement (NlU) as (Nl^c). (Nl^c) "The next time that B3 starts to be true, do S." Superimposing the universal quantifier WHENEVER, we are free to consider situations where B2 or B3 consists of several instances. (N15) "IF Bl WHENEVER B2 THEN S" Statement (N15) (usually expressed in the present in English, although clearly referring to all instances in the past) can be translated as (N15a). (N15a) "If Bl was true every time B2 started to be true, do S." 18 Similarly future imperative (Nl6) can be translated as (Nl6a). (Nl6) "WHENEVER B3 DO S" (Nl6a) "Every time that BJ starts being true, perform S." The only other problems occur in statements (N13) and (N15) when B2 is a condition whose value is always FALSE. Then statements (Nlj) and (N15) are analogous to statements (N17) and (Nl8). (N17) "Did it rain the last time you mowed your lawn? If so, do S." (Nl8) "Did it rain every time you mowed your lawn? If so, do S." The question in (N17) is like the "Did you stop beating your wife?" gag. That is, it is meaningful and has an answer if the person did at some time mow his lawn (or start beating his wife). But if not, it has no meaning, and can not be answered true or false. The reason for this meaninglessness is the existential quantifier itself. Although logicians are rarely unanimous, they do pretty much agree that existential quantification does affirm the existence of some thing. On the other hand, some natural language users would not feel unusual in saying that the question in (Nl8) should be answered YES if he has never mowed his lawn. This corresponds to the near agreement of the logicians that universal quantification does not affirm the existence of some thing, merely that, if there are any, they all do something together. There does not seem to be much difficulty about the non-existent time in the future, possibly because nothing in the future is certain. Thus, when Nelson said "I'll turn back when I see the order in this telescope," and then put the scope to his blind eye, nobody felt his statement to be meaningless. In fact, it had a very definite meaning. 19 U.2.2 Computer semantics In the following discussion, I use Bl, B2, and B3 to refer to Boolean expressions, S to refer to any statement, the small letter i to be an integer corresponding to a particular clause, and other characters to represent themselves. In particular, Fl, F2, ... are flag variables corresponding to clauses in conditionals, and El, E2, . . . are enable-flags corresponding to clauses in imperatives. We define computer semantics of the extended language by means of paraphrase in the original language (ALGOL 60). In a program, the i-th IF-clause of the form (la) or (lb) is to be replaced with an IF-clause of form (2). (la) IF Bl WHEN B2 (lb) IF Bl WHENEVER B2 (2) IF Fi (3a) IF B2 THEN Fi := Bl (3b) IF B2 THEN Fi := Fi & Bl In addition, after each statement which assigns a value to a free variable of B2, shall be inserted a statement of form (3a) or (3b). The free variable of B2 which was assigned a value is called a trigger variable, a statement in which it is changed is called a triggering statement, and the statement of form (3a) or (3b) is called the triggered statement. Similarly, the i-th statement of form (ka.) or (kh) is to be replaced by (5). 20 (ka) WHENEVER B3 DO S (lj.b) WHEN B3 DO S (5) Ei : = TRUE After every statement that assigns a value to any free variable of B3, shall be inserted a statement of either form (6a) or (6b). (6a) IF Ei THEN IF BJ THEN S (6b) IF Ei THEN BEGIN Ei := FALSE; IF B3 THEN S END Execution of (5) is said, to enable (6a) or (6b). k.2.3 Conflicts Unfortunately the computer semantics as described do not completely correspond to the human semantics desired. In particular these semantics can trigger the statements of form (3) or (6) in the middle of an instance of B2 or B3. That is, a statement assigning a value to a variable in B2 may not actually change the Boolean value of B2. Worse yet, such a statement might not even change the value of the variable assigned. So according to the human semantics defined, only one instance of B2 exists, •while the computer semantics recognizes several separate instances. Although it is possible to define more complicated insertions in place of forms (3) and (6) to implement exactly this interpretation of instances, I thought it wiser to start the experiment with the easier, less desirable, semantics. In order to handle the perception of instances of B2 adequately, the best scheme might be a trace-mode monitor, rather than insertion of statements. The computer semantics above do not mention what is to happen to values of variables and flags in insertions upon termination of the 21 invocation of a block in which they are defined. In fact, this causes several problems, in which the human has to fight the concept of hierarchical declaration and allocation of variables. The problems are: (i) If the free variables of B2 are defined at different levels, then at some level they are not all defined, and the inserted statements of form (3) will have no meaning in ALGOL 60. (ii) Even if the variables and flags of form (3) are all declared at the same level, the flags will lose their values upon exit from that block, even if the block will soon be reentered. (iii) What should we initialize the flags to, given that they must be reinitialized for each invocation? The only solution to problems (i) and (ii) is to declare the flags to be OWN variables. Unfortunately, OWN variables are not implemented by IBM ALGOL 60, nor by many other compilers. Even this solution does not treat the problem of inserting a statement containing unnameable free variables. The approach I followed was to allow insertions only where all the variables in the inserted statements were defined to be the same as those in the statement where the original time clause was. This is unfortunate, but as long as programs are not written that will bring this situation up, it is compatible with the intended semantics. Problem (iii), in keeping with discussion of the meaning of statements when nothing has happened, is solved by inserting (7a) or (7b) or (8a) or (8b) in the top of the block where Fi or Ei is declared. (7a) Fi := TRUE (or the null statement) (7b) Fi := TRUE (8a) Ei := FALSE (8b) Ei := FALSE 22 If the ALGOL implementation will actually trap statement (2) if the Fi is undefined, the best solution for statement (7a) is the null statement. Since few implementation designers consider themselves responsible for "undefined" values, I did not leave the flag "undefined", but rather made statement (7a) identical to (7b). Once again, this concession to the structure of ALGOL and IBM's implementation thereof does not help the correspendence between human and computer semantics. Another area in which details have not yet been specified is the area of parameter passing. If A is a triggering variable used in a procedure call as a parameter, should a statement like (3) or (6) be introduced in the procedure body after assignments to the corresponding formal parameter? If not, should we note which variables that could have been changed by the procedure actually were? Finally, should we assume that all variables used as parameters were changed by the procedure? These alternatives are caused principally by the concept of the procedure and parameter passing, which is a fairly complex area in which the perfect syntax and semantics have not really been worked out. If all parameters were passed by value, there would be no need to worry. However, if some parameters were passed by reference, it would probably be necessary to rely on a trace monitor to carry out the checks, or at least to note whether a variable was assigned a value or not. If some parameters are called by name, even more complicated problems can ensue. One further alternative is to find out in some way (probably by trace monitor) which variables were changed, but not do anything about it until the procedure is completed. In this way, we shrink the time spent by that procedure down to a point (with respect to the triggering variable) and the time expressions are unambiguous. In fact, the value of the variable would be the value attained 23 at the end of the procedure call, rather than the beginning. It would he nice to think of other ways of passing parameters to procedures, because it is a concept that makes new features harder and harder to add to languages . k.2.k Questions of implementation Several features of triggered variables remain to be worked out. First, consider a statement of form (6) in which S contains an assignment to one of the triggering variables in B3« (1) Since this triggers the variable again, before the first triggered statement is completed, should this situation be allowed? If not, how do we disallow it, since the chain of triggerings is potentially quite complicated? Probably the existence of a trace-mode monitor would make possible (a) immediate interrupt and execution of the new triggering, (b) delayed action on the triggering until the previous statement is done, or (c) termination. (2) If such situations will be allowed to execute, and a variable triggers several statements at once, in what order should they be performed? In principle, I feel they should be simultaneous, with no side-effects. The ALGOL 68 concept of simultaneity (the order of simultaneous events is undefined) is an affront to users, but do we put in its place: (a) some specific order, and tell the user what order was chosen; (b ) an order that avoids side-effects if possible, else error message; or (c) a strict simultaneity regime in which all statements start with identical copies of the relevant variables, so that no statement interferes with the values needed for any other statement? It is quite clear from this problem that eneral implementation of time denotation absolutely requires a recogni- tion and consistent implementation of simultaneity. 2k (3) If a variable triggers a statement that retriggers the variable, should we consider this second triggering a recursive call, or merely an iterative GOTO? In either case, are we willing to allow infinite retriggering? I believe that if the language allows constructions that could lead to infinite iteration, it should not disallow infinite recursion. (k) Do we want such a situation to be one of recursion or iteration? That is, considering the inserted statement (6), should it contain a test of the triggering variable, and a GOTO itself? Or should it merely call a recursive version of itself? In this implementation, I chose the recursion, because it was easier to implement, and it might be more reasonable. It is certainly hard to relate to natural language on this matter, just because natural language is rarely spoken in great depths of recursion. This problem, unlike most of the others, is a direct result of the intended feature of the language, and deserves careful study. k . 2 . 5 Summary of semantics For various reasons, most of which depend on already existent but not elegant features of ALGOL and most such languages, it is difficult to implement computer semantics for this language which correspond exactly to the desired human semantics. In particular, the implementation I have chosen is characterized by the following unfortunate discrepancies: (1) Not all instances of a triggering variable actually do trigger anything . (2) Wot all information stored as a result of triggering or execution is remembered for all time. <; 25 (3) It is certain to be unclear, unintuitive, or at least accidental, what happens when more than one set of statements is triggered at once, unless some provision for simultaneity is also incorporated in the language. (k) Not all triggering takes place immediately. 26 5. IMPLEMENTATION Salient characteristics of the language are: (1) All of the inserted sets and checks are bound to slow down execution. (2) In case of a more thorough treatment of the initialization of flags, etc., some quite complex bookkeeping might need to be done at run time. Because the nature of this experiment was to propose and investigate some purportedly useful features of programming languages, and to demonstrate their possibility, these problems, which deal with detailed practicality, were not deemed important enough to warrant complicating the task anymore. The main question was, can a preprocessor be built that will implement this language, and can this language then be used? Clearly a more thorough attempt at efficient implementation should be made before using it in production, and in any case, the benefits have to be measured and weighed against the increased cost per run before deciding to speed it up. Possible modes of implementation considered were: modification to an existing ALGOL 60 compiler; a preprocessor producing output in ALGOL 60; a run-time trace monitor, needing modifications in an ALGOL 60 compiler as well as a preprocessor pass.. This last method might be the best for both speed and ease of modification. However the simple preprocessor is the easiest for initial programming, and is the mode chosen for this study. 27 Features of the language that are interesting for preprocessing are: (1) Variables can be used and assigned before appearing in a triggering context. (2) There is a possibility of a WHEN clause inside another WHEN clause. (3) Infinite insertions must be avoided in case of a recursive triggering. (k) A flag has to be declared and initialized in the outermost block consistent with the scope of the variables in its corresponding checks and sets. (5) Trigger variables are to be checked in all procedures in which they are defined, even in the procedure triggered. (6) A FOR-loop, a procedure body, and a THEN clause each consist of exactly one (possibly compound) statement. If such a statement triggers something, another statement must be inserted after the first statement, while retaining the single-statement property of the clause. (7) If a FOR-loop index is a triggering variable, the loop statement should be preceded by the inserted check. (8) A statement should be inserted after each procedure call which has trigger variables for parameters. (9) All variables of a multiple assignment statement need to be checked. 5.1 Overall nature of the preprocessor Since it seemed that all of the above statements involved locating assigned variables, and inserting statements associated with those variables, 28 I implemented one pass (called pass 2) which did only that. It requires as input a valid ALGOL 60 program with the constraints that declarations of a block have to he sorted with procedures last. Furthermore, it takes a new declaraction, called the CHECK declaration, which has to occur just before the batch of procedure declarations. This lists a procedure and a list of variables which trigger this procedure. Such a prepass is useful for debugging anyway, and in fact PL/I and FORTRAN implementations sometimes have something of this sort, although in different forms. A somewhat more general scheme, provision of a routine triggered by access and a routine triggered by storage, has also been suggested for addition to simulation languages. The effect of this prepass is to turn the CHECK into a comment, and to insert procedure calls after every assignment (of any sort) to the triggering variables inside the block declared. The restriction of order in the declarations makes possible the introduction of procedure calls in mutually triggering routines. The first pass Is now required to read the language as defined in section k and turn a program written in it into valid input to pass 2. To do this, it needs to find WHEN and WHENEVER clauses, replace them with checks or enables (statements (2) and (5)), construct the procedures out of statements (3) and (6), construct free variable name lists, construct the flag initializations and declarations, worry about scope of variables, and turn simple statements into compound statements with bracketing BEGINs and ENDs. The only gimmick here is that, as the two passes have most of their program In common, due to parsing and scope checking, they share the same code, and execute concurrently. At the end of the pass through the data (the expanded language program), if the program found time expressions, 29 then it decides it was a pass 1, performs the insertions of pass 1, and prepares to start pass 2. If it did not find any WHEN clauses or WHEREVER clauses, it decides that it must have "been pass 2 and performs the insertions for pass 2, and terminates. The fact that this extreme overlap exists indicates that some radically different organization, such as direct compilation, would be much faster for producing the same result. 5.2 Details of implementation The two passes have a lot in common. There is a top-down recursive descent parser, written in SNOBOlA (running on the SPITBOL compiler), which reads in an entire program as a string. It tries to find a program, and if it succeeds, it quits. Each construct of the language corresponds to a routine, which is called with an initial cursor position. The routine finds the longest instance of that construct starting at that cursor position, and returns the ending cursor position. If it does not find an instance, it performs a failure return, which is sensed by the calling routine. In SNOBOL, it would have been wiser to write the low-level constructs as patterns rather than as routines, but for historical reasons this was impossible. Each routine corresponding to a declaration type puts all declared identifiers on a stacked list corresponding to the depth of the block. Since a block is similar to a procedure, the formal parameter routine acts similarly to the declaration routine. When a block (or procedure) is entered by the preprocessor, the stack of lists is pushed down, and when the block is left, the stack is popped up. The first pass uses routines for new constructs in addition to requiring modifications of existing routines. The first pass looks for time clauses, and prepares to make insertions only after successfully 30 matching the entire block in which an insertion will be needed. Upon successfully matching a statement of the form (l), pass 1 invents a new flag, replaces (l) with (2), produces a new procedure of the form (3), then searches up the stack of declared variables until it finds the innermost block in which one of the variables of Bl or B2 is found. This is the level at which the insertions of declarations, checks, procedures, and initializations must eventually go. Since we do not yet know where the end of that block is, we do not actually perform the insertion. When that block finally is successfully matched, it looks on a list of insertions at that level. If there are any, it notes the end of the block, and puts these insertion-records on a permanent list of such insertions. After the entire pass is completed, the insertions will be sorted and then performed. Another feature of this pass is that, by delaying replacement of the WHEN clause until the THEN is reached, any embedded WHEW clauses will be replaced first. Upon successfully matching a statement of type {h), pass 1 invents an enable-flag name, creates a procedure of the form (6), replaces the entire statement by a statement of type (5), creates initializations, declarations, and CHECK declarations with the free variables of B3« Then pass 1 searches up the stack of declaration lists, looking for the inner- most block in which one of the variables of B3 is defined. All these created insertion records are kept as in the above case. When a block is closed, several clauses may have affected it, so it is at that time that the details of where to insert which statements are worked out. The insertions are not actually performed, however, until the end of the pass. The second pass turns CHECK declarations into comments and notes the procedures and their associated variables. The routines for 31 assignment statement and FOR-clause were modified so that each trigger variable assigned a value is found and associated with all procedures that it triggers. Since a variable may trigger routines at various levels, this search requires looking alternately at the check declaration variable list and the declared variable list for each block going outward. The search stops on finding the level at which it was declared. These routines also keep track of the locations of the beginning and end of their respective statements, so that the location of the BEGIN-END pair needed to convert it into a compound statement is known. This pass also produces insertion records, but since no information is needed about the location of beginning or end of block, they are put out immediately. 32 6. EXPERIENCES IN USING THE LANGUAGE 6.1 A program using the future imperative Having performed the described implementation, I "wanted to use the language to see what its good and had points really were. A task exemplifying the good points was a modification of an algorithm by Knuth, for the addition of polynomials represented as circular lists. The terms are in decreasing order of exponent, and a final term of exponent -1 is the list header. In this algorithm, five pointers are used. P and Q are pointers to two different polynomials, of which the first is to be added to the second. 0,1 follows Q around, marking the end of the completed addition. 02 is used as a temporary In creation and deletion of list elements. AVAIL Is the head of the available space list. The differences in the algorithms appear small. The WHENEVER clause renders four statements in Smith' s algorithm superfluous, but this makes the program no shorter. However, it makes the algorithm somewhat easier to read and perhaps to modify. In the new algorithm, Q is clearly and absolutely dependent on Ql. Although Knuth considers the important thing, and 01 an artifact of the typical list processing situation, it is just as plausible to consider Ql to be most important (the end of the completed portion of the addition), with Q the tentative next term. To restate the algorithm this way, Ql steps through the completed sum polynomial, with P and Q referring to tentative next terms on their respective polynomial. However we see it, there is an advantage in writing the algorithm this way: the WHENEVER clause states the general condition that Knuth puts in 33 Comment Initialize; Comment Compare; Comment Accum. coefs: Comment Delete; Comment Insert new: Al: p:= link(p) ql:= q q: = link(q) A2: if abc(p) < abc(q), then begin ql:= q; q:= link(q); goto A2 end- else if abc(p) = abc(q) then goto A3 else if abc(p) > abc(q) then goto A5; A3: if abc(p) < 0, then goto ENDPOLYAD else coef(q):= coef(q) + coef(p); if coef (q) = then goto Ak else begin ql:= q; p:= link(p); q:= link(q); goto A2 end; Ak: q2:= q; link(ql):= q:= link(q); link(q2):= avail; avail := q2; p:= link(p); goto A2; A5: q2:= avail; avai 1 : = link ( avail ) ; coef(q2):= coef(p); abc(q2) := abc(p) ; link(q2):= q; link(ql):= q2; ql:= q2; p:= link(p); goto A2; ENDPOLYAD: end Figure 1. Knuth's algorithm 3h BEGIN Comment Initialize; Comment Compare; Whenever q j- link(ql) do if link(ql)=link(q)7" then q:= link(q) else ql:= link(ql); Al: p:= link(p); ql:= q; A2: if abc(p) < abc(q) then begin ql:= q; goto A2 end else if abc(p) = abc(q) then got o A3 else if abc(p) > abc(q) then goto 5; C omment Accum. coefs; A3 if abc(p) < 0, then goto ENDPOLYAD else coef(q):= coef(q) + coef(p); if coef(q) = 0, then goto A^+ else begin ql:= q; p:= link(p); got o A2 end: Comment Delete; Comment Insert new; Ak: q2:= q; link(ql):= link(q); link(q2):= avail; avail := q2; p:= link(p); goto A2; A5: q2:= avail; avail := link (avail) coef(q2):= coef(p); abc(q2) := abc(p); link(q2):= q; link(ql):= q2; p:= link(p); goto A2; ENDPOLYAD : end Figure 2. Smith's algorithm expressed with a WHENEVER clause 35 his comments — Ql follows Q around (or Q always leads Ql). Although this is not clear in the original algorithm, the rewritten form explicitly states, in the WHENEVER clause, that if the condition becomes false, Q or Ql shall be advanced until the condition is true again. They cannot advance backward, so in any given case, only one of them should move. We are guaranteed that Q starts ahead, because the first statement says that they start out together, and we know from the WHENEVER clause that in that case, Q, advances. Since Ql advances only one at a time, Q can never get behind Ql, without first being even with it. In such a case the clause takes over, forcing Q ahead. Only if Q gets too far ahead will Ql come up to find it. Since Q is not an independent entity, but just a shadow of Ql, statements involving its location are not interesting. The method of polynomial addition is easier to read with the Q bookkeeping factored out into one place in the program. Being told that Q and Ql will move themselves so as to have Q leading Ql is all the reader needs to know about Q's location. Now he can concentrate on the cases of comparison, the insertions, deletions, and termination. Statement A2 compares the two tentative next entries, referred to by P and Q. One branch finds the Q entry to be next, and moves the completion pointer (Ql) to Q's location. Now go back to A2. Since Q is always just ahead of Ql, the reader knows that this comparison is with the next tentative entry beyond Ql. If the two tentative entries have the same exponent, then their coefficients are added at A^. Unless the coefficient of the sum is 0, the completion pointer is advanced to Q's position, P is advanced, and a new comparison made. However, if the coefficient was zero, the entry Q is not part of the sum, and should be 36 deleted from the list. This entails that the completion pointer not move, hut the cell it refers to advance its pointer over the current Q. Since 02 has been told where the deletable cell is, it will he deleted. This means that Q points to a cell that is to be deleted, rather than the cell beyond the completed sum. Naturally, since this violates the condition that Q always lead Ql, we assume that is advanced out of the cul-de-sac. Then the cell is deleted and the P term skipped, as having been used. The only other case in the comparison is where the P cell is actually the next term. This means that it has to be copied into the list, right in front of 0,1 which will then, as the completion marker, advance to the copied cell. So a new cell is acquired, pointed to by Q2; it is given exponent and coefficient, and its link is to the cell referred to by Q. So far it is not in the circular list. Now we change the link in the cell referred to by 01 to point to the new cell. Thus, all we have specified is the place where the new cell goes, by definiting the link outward and the link outward inward. The automatic sensing of the 01-0 separation moves Ql naturally to take up the slack. Then P is advanced and another comparison made. 6.1.1 Benefits of the language In this program, the WHENEVER clause yielded 3 benefits: (l) the complete dependence of Q on Ql is shown, (2) one clause demonstrates the requirement that Q remains just ahead of Ql, rather than scatter this information all over the algorithm, and (3) bookkeeping statements can be deleted, contributing to the readability of the algorithm. These benefits were bought at the price of efficiency. The inefficiency resides in a couple of unnecessary checks in the algorithm, and a check in the check- procedure itself. In principle, especially with a trace-mode implementation, 37 these checks need not be slow, but in this recursive function call implementation, they are. Most of the checks are not inefficiencies, as they have to be made anyway. There is one case where the new algorithm has an added efficiency. In case Q originally starts pointing at the zero polynomial (a header pointing to itself, and no other cell), Knuth's algorithm advances Q unnecessarily. All that is necessary is that 01 start equal to Q. Now if 01 refers to a node not following Q, then Q, will move, by virtue of the general condition expressed in the WHENEVER clause. However, if Q, is the zero polynomial, then 01 refers to a node which follows (by circularity) the node referred to by Q. Thus Q, does not need to advance. Both algorithms work successfully if either polynomial or both polym inials are zero, or if the sum of the two polynomials is zero. 6.2 An example of the past conditional The next example looked easy to make, but turned out not to be. The difficulty is instructive. The plan was to make a tape editor for selecting out certain records according to the characteristics not merely of the chosen records, but also of previous records. Thus we assume the tape to contain information in some sort of hierarchical order, where previous elements can change the mode of interpretation or value of later elements. The basic loop reads in all the fields of a record, and decides whether to transmit it. The program would presumably ask something like, "IF RANK was above CORPORAL WHEN TIME_ACTIVE was greater than 6 years, do something." (Perhaps this was selecting an intentionally heterogeneous cross-section of the file, not wishing to cluster too many corporals or something like that.) But to get the desired result, we have to avoid reading RANK after TIME_ACTIVE, or else the RANK compared will be the rank of the previous record. But what if the RANK field follows the TIME_ACTIVE field? 38 6.2.1 A "basic difficulty exposed Tasks of this sort pose a fundamental problem. A programmer computing some attributes after others, would no longer be sure, when writing a statement referring to a past value, which past value would actually be referred to. This problem could be alleviated either by (l) computing the values all at once, using some sort of simultaneity feature, or (2) performing the computation of all attributes inside a procedure whose only purpose is to delay the triggering of the various checking procedures until after it is finished. Certainly it is unwise to burden the concept of procedure with this unrelated responsiblity, so a new feature may be necessary: a way of compressing the time spent in a section of code down to a point. Although this is reminiscent of the suggestion for simultaneity, the concepts are independent. In a stretch of code held to be executed simultaneously, the values of variables do not change until the code is completed. But in a stretch of code held to be executed in a very short period of time, the values of variables might change, but such changes would not be allowed to trigger anything until the code is completed. 6.2.2 The example program At any rate, redesigning the problem so that this order-sensitive effect would not occur took most of the life out of the example. A more complicated example, drawn from actual life of programming, would wind up so complicated that (l) it would not be clear that the suggested solution would help, and (2) one might feel that the program could be drastically redesigned anyway. The new problem is: a file of records is to be read, and a subset selected out. After all the fields of a record are read in, the record is assigned a type based on these fields. Then this record is 39 "begin for i:= 1 step 1 until numberofrecords do read a,b, c,d, e; type:= (if a > 3 & b = 1 then 2 else if a > 3 then h- else c) ; if a > k when type = 3 then if e == 3 0£ e = k then goto output; if a < ^ iL^en type = 2 then if e = 2 then goto output; print a,b, c, d, e, 'deleted'; goto past output; output: print a,b,c,d, e, 'transferred'; pastoutput: end ; Figure 3» Sample program for WHEN clause-conditional 1+0 passed on for transmission according to conditions on its fields and on fields of past times, when type had some particular value. Note the computation of the type. It depends on values of the A- and B fields. Under certain circumstances, it can take on any value at all (the C field). The program says that between a record of type 3 having A above k and the next record of type 3, all records having E of 3 or 4 will be transmitted. Similarly, between a record of type 2 having A below 2 and the next record of type 2, all records having E of 2 will be trans- mitted. Due to a programming oversight, it is impossible for there to be a record of type 3 with A above k. The only plausible condition in this artificial program is the type 2 record with A lower than or greater than 2. These conditions are not present conditions; that is, it is irrelevant what A or TYPE is in the current record. 6.2.3 Advantages of time-expressions In order to write this in a standard language, a programmer must make up a new variable. This mode-switch can then be checked in place of the past condition. However, the reader of the program then would have little way of knowing what the condition is, and how it could come about. If the past condition was replaced with a flag, the reader would be forced to find all references to it. Since many programs are submitted many times, it is generally not reasonable to expect that the programmer will ask for a cross-reference listing every time. One reason is laziness, but the other is that anything that changes so little from one run to the next is truly unin formative, in the information theoretic sense. As it is generally pointless to look at uninformative material for information, the programmer will rarely bother to get a cross-reference listing to check to whether a given flag is set 2 times or 3. If the flag is actually an hi integral and meaningful piece of information in his program, he will doubtless want it to be a flag. But if the flag represents a particular kind of information that can be expressed as a condition at a past time, it is more clear to express that condition directly. This saves the problems of (l) forgetting to check it in one place where it could happen, (2) not checking in a place where it "couldn't happen," (j) setting it, thinking of it as a different flag, and (k) using that flag to save space for another flag in a disjoint part of the program. k2 7. SUMMARY AND FUTURE PROPOSALS Hopefully, these examples and explanations have given an idea of the good and bad points of this language - an d- implement at i on. The good points are mainly psychological effects on the programmer: ability to state conditions at relevant places, and to state the relevant conditions; ability to avoid writing some redundant or repetitive or uninformative material; modularization because of ability to isolate general conditions into one statement. The bad points generally are discordances between the language and natural thought: the inherent problem of early or late triggering; the problems caused by interaction of effects of scope and recur sivity with the proposed time feature; execution inefficiency. Most of these positive and negative aspects are difficult to measure or estimate. The only conceivable approach is to actually test it with real programmers of some sort. In addition, there have been proposed partial solutions to most of the problems, many of which require the testing of yet new and additional features. However, much could still be done even with simple features of the type implemented here. Consider the UNTIL or WHILE clause of FOR-loops in ALGOL and PL/I. As currently defined, the named condition terminates the h3 loop at the beginning of some iteration. An analogous construction (which should really use the words UNTIL and WHILE) could be defined, for which the named condition terminates the loop in the middle of the iteration at the point at which it goes true (or false). Additional constructions using time denotation are BEFORE, AFTER, WHILE, SINCE clauses. Each of these names a time, and the times could be used in the past or future, depending on the type. A further approach would be to create various debugging features such as LIMITS (VAR, EXP1, EXP2) which informs the system that if VAR goes out of the bounds EXP1, EXP2, a diagnostic message should be written. JLIOGRAPHIC DATA K ET ~ it le and Subtitle 1. Report No. UIUCDCS-R-73-560 The Concept of Time in Programming Languages 3. Recipient's Accession No. 5. Report Date January, 1973 uthor(s) Edward NeufYille Adams III 8. Performing Organization Rept. No. erforming Organization Name and Address Department of Computer Science University of Illinois Urbana, Illinois 10. Project/Task/Work Unit No. 11. Contract/Grant No. none 2 Sponsoring Organization Name and Address Department of Computer Science University of Illinois Urbana, Illinois 13. Type of Report & Period Covered 14. 5 Supplementary Notes 6Abstracts There are many semantic features of natural languages which are not represented in programming languages. Some of these features involve the concept of time . The features of tense, aspect, quantification, and order seem to be useful in natural language, and might be useful in programming languages also. As an experiment, a new language is defined by embedding the distinction between future and past, and time quantification both universal ( v/henever ) and existential (when ) into ALGOL-60. These constructions add nothing to the power of the language, but add to human understanding of programs. A preprocessor is described for implementing this language, and a few programs written in the language are discussed. The advantages and disadvantages of using this language-and-implementation are discussed in detail. 7.Cey Words and Document Analysis. 17a. Descriptors 17t Identifiers/Open-Ended Ter 17c f OSATI Field/Group '8- va, labil.ty Statement ■release unlimited F °f NTIS-3B 110-70) 19. Security Class (This Report) UNCLASSIFIED 20. Security Class (This Page UNCLASSIFIED 21. No. of Pages 22. Price USCOMM-DC 40329-P7I *v S> A*> °ct a 19/3