Page:AIM-443.djvu/17

 -- there may exist more than one programming language construct which can embody that concept in a reasonably natural manner. Furthermore, it sometimes requires more than one construct to properly embody a given concept. For example,  would be utterly useless in expressing iteration if some form of assignment statement (or other side effect) were not also used!

In understanding (a piece of) a program it is necessary to determine not only what construct is being used, but the concept it is intended to express. While we may prefer to think of a procedure call as meaning "go do this other module and then come back", this is only one possible interpretation. We must realize that there are situations where coming back doesn't matter (i.e. the tail-recursive cases), and these situations may be exploited. Just as a concept such as modularity may be expressed by diverse constructs, so may a language construct be interpreted in various ways, some of which may lead to superior compilation techniques. {Note Various Optimizations} One example of this is the tail-recursive procedure call; another is the logical expression occurring in the predicate of a conditional, which does not actually have to produce a Boolean value when compiled (this is called "anchor pointing" in [All72]).

It is not unreasonable to want to be able to infer the intent of a piece of code from the particular construct used to express it. If only  is used to express all control structure, it is hard to identify the conceptually important notions of conditional, iteration, and escape occurring in the program. It is important that alternative modes of expression exist; but the mere banishing of one abused alternative is unlikely of itself to cause correct usage of the others. Instead, a language should be so designed that one is encouraged to use a construct if, and only if, it is appropriate; it mostmust [sic] also provide enough constructs to cover all reasonable programming concepts. Otherwise, programmers will merely misuse the constructs they are given (and most programmers are very good at this!). The structure of a program is dictated largely by the structure of the problem. If the problem solution is best expressed as a finite-state automaton, for example, then no amount of structured camouflage will help that fact.

This is the essential frustration we have experienced with. We discovered that  was often being used even in situations where more appropriate and expressive constructs were available, and that it was being used for all sorts of purposes we hadn't anticipated, and so we sought to eliminate unwanted concepts and programming styles by banning a construct. This is as fruitless as eliminating iteration by banning -loops would be (people would just use   or procedure calls), or eliminating recursion by banning procedure calls (people would, and do, simulate it by using an array as a stack). We need to get a better grasp on organizational concepts and their relationship to the individual constructs which make up our languages.

Conclusions

Procedure calls are demonstrably not inherently as inefficient as computing folklore would lead us to believe. There are implementations of higher-level programming languages in which procedure calls are almost as cheap as  statements.

Not all procedure calls need push a return address. "Tail-recursive" procedure calls (those occurring at the end of a procedure) can be compiled almost as if they were simple  statements. In fact, procedure calls can uniformly be treated as  statements which pass parameters, with return addresses being pushed at a conceptually different point (the commencement of argument evaluation). Such a technique reduces the amount of stack space