Extending PL/I for structured programming

  • View
    212

  • Download
    0

Embed Size (px)

Transcript

  • Computer Languages, Vol. 1, pp. 29--43. Pergamon Press, 1975. Printed in Northern Ireland

    EXTENDING PL/I FOR STRUCTURED PROGRAMMINGt

    JOSEPH E. SULLIVAN The Mitre Corporation, Bedford, MA 01730, U.S.A.

    (Received 5 November 1973 and in revised form 17 January 1974)

    Abstract---General principles of structured programming, and practical aspects of applying those principles within the programming language PL/I, are discussed. Suitable extensions (and contractions) of that language are suggested.

    Structured programming PL/I Hierarchial design Level of abstraction Program com- prehension Programming language design GOTO-free coding Interrupts Error handling

    1. WHAT IS STRUCTURED PROGRAMMING?

    1.1. The basic idea

    THE TERM "structured programming" embraces a number of related notions, most of them neither new nor surprising. The fundamental concept is that programs consists of layers, as it were, each layer implementing a characteristic level of abstraction built upon the layers below and supporting the layers above (e.g. Dijkstra [1]). Each element at a given level is a distinct unit with a clean and in some sense minimal interface to other units, and may be independently designed, implemented and tested. Thus far, this may seem to be very little more than the common concept of "modularity," a term now so shopworn that the phase "modular design" is something of a private joke among programmers. Where structured programming departs from platitudes is in prescribing two specific design and coding rules.

    1.2. Top-down, hierarchial organization

    First, design and coding are blended into a single, top--down process. That is, the entire system operation is expressed as a simple process in terms of other, probably as yet un- defined, processes and data structures (e.g. "obtain user's query" or "look up string in directory"). The next step is to take one or more of these abstractions and define it, possibly in terms of still other abstractions, and so on until everything is resolved into processes and data structures already supported in the implementation environment. The trick is to keep each expansion minimal---some would even demand that it fit on one page [2]--so that the algorithm is easily designed and understood (cf. Dijkstra [3, 4]).

    Of course, the controversy surrounding this design method centers on the question of practicality. In fact, no one denies that the success of the method requires foresight during the topmost design phases, in order to retain elegance, not to mention efficiency, at the bottom. This is partly because one generally has a fixed goal in terms of the existing machine, operating system, language and subroutine packages towards which he must direct his design as a practical matter. In addition, poor choices for abstractions at the higher level show up in the lower levels as excessive, seemingly incoherent, interdependeneies. In practice, good designing is typically an iterative process--starting from the top down, one

    1 This paper is an adaptation and updating of report ESD-TR-72-315 of the Air Force Electronics Systems Division. This work was performed under AFIESD Contract F 19628-71-C-0002.

    29

  • 30 JOSEPH E. SULLWAN

    realizes at some point he had made a bad choice at some higher level, possibly even the very top, backs up to that point and starts down again. (Of course, there is nothing really wrong with retaining the work one did during the first descent, to the extent that it still applies, although this is probably where many errors are born.) In short, we are rarely all that sure of what we want before we see what it involves. On the other hand, good designs always look strictly top-down when finished; the proof of the structure is in the compre- hension.

    1.3. Canonical composition rules The second rule is that the user restrict himself to one of a small number of"composition

    rules" in expressing the control logic of each unit. In the earliest and most basic formula- tion, the flow chart of a process is restricted to one of the three forms:

    > P1 > P2 * (Sequence)

    > (Alternation)

    > t 1 (Iteration)

    The t's are elementary 2-branch tests. Each P may be null, an elementary process, or itself a process whose chart is of one of the three types, as it will be noted that each form has but one external entry and one external exit. It is easy to see how this process of expansion is related to the concept of levels.

    In relation to the programming language PL]I, these flow charts correspond to simple statement-to-statement sequence, IF-THEN-ELSE constructions, and DO-WHILE groups, respectively. Thus it is already possible to program in a structured manner in PL/I without resorting to GO-TO statements, which Dijkstra [5] would banish them from programming languages because their presence invites composing non-structured programs. However, it must be recognized that as things stand, PL/I sometimes exacts what seem to be an un- reasonably high price in elegance, efficiency, or both, for perfect avoidance of GO-TOs; this is the subject matter of the third section of this paper. Of course, even if the programmer should appeal to GO-TOs in such cases, he may still conform to the canonical composition rules.

    BShm and Jacopini [6] have proved that any general flow chart may be transformed so as to involve composition of the three basic forms only, by spfitting nodes (copying boxes) and introducing auxiliary variables where necessary.t Actually, their theorem was stronger than this, in that only two forms (sequence and iteration) are necessary if an operation amounting to "save result of alternative test" is available. In PL/I, a logical (BIT(I))

    1" Knuth and Floyd [7], implicitly ruling out the possibility of introducing auxiliary variables and com- putations, exhibited a program which could not be rewritten without either GO-TOs or procedure calls. With auxiliary variables, however, the program is easily rewritten in canonical form.

  • Extending PL/I for structured programming

    assignment is such an operation; thus any alternation

    31

    could be rewritten

    IF (condition) THEN (action 1); ELSE (action 2);

    DCL (FLAGY, FLAGN) BIT (1); FLAGY = (condition);

    FLAGN = ---nFLAGY; DO WHILE (FLAGY);

    (action 1) FLAGY = '0'B; END;

    DO WHILE (FLAGN); (action 2) FLAGN = '0'B; END;

    Although such a result might be of interest if one is searching for the minimum number of control primitives necessary for programming, this is not the point of structured programm- ing. On the contrary, the goal should be to provide a rich lexicon of control structures, as long as each such structure is separately useful and well understood--i.e, theoretically reducible to the basic structures by a simple chain of reasoning. There are several such alternatives or generalizations for the canonical forms which do no violence to the concept of structure.

    The extension to generalized sequence is immediate:

    )P, )P~ ) . . . )P~ )

    Similarly, n-way or generalized alternation

    /'1

    is easily seen to be equivalent to

    _-- -r-[ = v l

    (Ti:v2) n Y~_ P"-' ~. . . . . (Tt=vn_l)

    Pn

  • 32 JosEPh E. Strdav.~t

    Finally, the generalized iterationt

    I f " PI - -"t l ~P2 ---'-t2

    t " P5 . . . . . . . Pn "-''m~i'n'-''m''Pi'1"l'l

    J

    is reducible to (note the new boolean variable G):

    In " - - " -Q I " - - ' - G ? - - - " PI

    ~,m2 .Q;___._ .... .,)~

    2 2 "--..~ 02---"2 " I

    where QI and Q2 are assignments of "yes" and "no," respectively, to G.

    2. WHY STRUCTURED PROGRAMMING?

    2.1. Understanding programs The principal rationale for structured programming is an improvement in the ease of

    understanding the dynamic process represented by a program. This extends along three fronts: comprehension by a programmer examining the program text, proof of correctness of debugging of the process, and explanation of the system function to a user.

    2.1.1. Comprehension by a programmer. Comprehension of the (static) program text, in relation to the dynamic process it evokes, is improved because everything that logically (dynamically) precedes a given program point will also precede it physically in the source text, except possibly for ongoing iterations whose extents are also highly visible in the source. Or, as Dijkstra [3, 5] put it, one can define any state of a structured process as a stack of pointers to the text and iteration counters. That is, all one needs to know at any point in time during execution of the procedure is: where control is in the current lowermost block, where that block was invoked by the one above it, and so on, plus a loop counter for each iterative block. At any given level, a single such textual index thus suffices to define the state of progress, where otherwise a complete record of all branches taken would be necessary. Consequently, it becomes much easier to be sure, when reading the static text of the program, what dynamic context is assumed i.e. "what do the variables mean when I get here--and by what path did I get here anyhow?"

    Otherwise, if GO-TOs and labels are used freely, it can easily become necessary for a reader to understand the whole program simultaneously in order to understand any part of it In a sense, it is the labels and not their correlative GO-TOs that are mainly at fault in a such a case.

    That is, the "arrive here from someplace" operation implied by a label is more dangerous within its environment than the "depart" is within its (immediate) environment. This is

    I" This chart, with Px null, is called omega, by Brhm and Jacopini [6]. Kosaraju ]8] proved that omega. cannot be reduced to a composition of