Teaching the Fatal Disease

(or)

Introductory Computer Programming Using PL/I

 

Richard C. Holt

Dept. of Computer Science and Computer Systems Research Group, University of Toronto

5 November 1972

 

Prescript

This paper enjoyed surprisingly wide circulation in the early 1970's. It was translated into a number of languages (natural languages!). It was the time when some people felt that PL/I might be the mother-of-all-languages, and would become the dominant programming language of all time. At the time, there was considerable hope that good programming languages would ease the programming effort and make programmers more productive (this should be the case, if we could only get good languages accepted). We are now in the age in which the common belief is that "all languages are the same" (this is the false doctrine that a programmer is little affected by the programming language he or she is stuck with.) One irony is that PL/I is in many ways better than currently dominant languages like C and C++; at least PL/I knew how to check a subscript! This paper has been re-entered in 1999, unchanged from its original 1972 version, as a result of a recent request for a copy of it.

Richard C. Holt, 30 March 1999

 

Abstract

This paper discusses the problems and advantages of using PL/I as an introductory programming language. The abhorrence of the author for certain qualities of PL/I 's divulged. The economico-academico-IBMical inevitability of this programming language is touched upon. Finally, and of some possible worth, certain tenets of self discipline which are required when using this language for teaching (or anything else) are explored.

 

 

"If Fortran has been called an infantile disorder, PL/I must be classified as a fatal disease. "

-Edsger Dijkstra in Introduction to the Art of Computer Programming

1. Programming Language One

            It was called NPL (New Programming Language) but NPL (National Physics Laboratory) objected too loudly so it was called PL/I which is not P.L.I. (Programming Language for IBM) or P times L divided by I but Programming Language One. The I stands for One, you see, and I can not imagine what the One might mean.

            PL/I was developed by a committee. You have heard of the committee which designed the ideal animal. The expert on noses selected an elephant's trunk. The expert on arms gave the animal the grace of dove's wings, and so on. The product exhibits some lovely features but, unfortunately, the expression

              25 + 1/3

yields 5.33333333333333 and the loop

              DO I = 1 TO 32/2 ;

                   Statements

              END;

is executed zero times.

            And yet, it is better than Fortran. Not that Fortran, with its antediluvian control structures, e.g.,

     GOTO I,(1,2,3,92)

(don't forget the first comma!) is difficult to surpass.

            Those of us at universities with IBM 360 machines, were saved (perhaps) from IBM's marketing force by University of Waterloo's fast Fortran compiler (WATFOR). That compiler made it economically feasible to teach a "real" (warts and all) programming language to thousands upon thousands of bright, eager university students. It has also helped to entrench Fortran as the de facto programming lingua franca, leaving a generation of students thinking that GOTO I,(1,2,3,92) is a reasonable way to express oneself.

            One of the good things about PL/I is the fact that it introduced some of the advanced features of an exotic European programming language (ALGOL). For example, ALGOL has the feature that semi-colons separate statements PL/I considerably advanced the dissemination of semi-colons by requiring them in intriguing new places, such as after END (so you can tell where END ends).

            PL/I alleviated many bothersome old time Fortran hangups such as restrictions upon expressions used for subscripts. PL/I cleaned up and simplified a number of features of Fortran, such as input/output format lists. There is no doubt in this author's mind (honestly!) that PL/I, compared to Fortran, is a nicer, easier, more advanced and better language to teach and use.

The PL/C compiler for PL/I now provides a compilation/execution cost per student job, which is comparable to (though greater than) WATFOR. This now allows IBMical universities to extract themselves from Fortran and to teach a better, but still "real" introductory programming language. Several major universities have already taken this option, and it is likely- that more will in the future.

Let me say why PL/I Is "real". There are only three real programming languages:

  • FORTRAN - because everyone knows it or knows of it.
  • COBOL - because it runs- more programs.
  • PL/I - because it is supported by IBM and is nicer than the above two.

ALGOL never rose above (fell beneath?) its academic non-I/O roots. A host of other languages, e.g., Snobol, Simula,..., are important but much more difficult to defend as relevant to, say, the job market. The new language Pascal is my own favourite programming language, but time will tell if it can fall beneath its academic roots.

 

2. An Economico-Academico-IBMical Inevitability

PL/I is better than Fortran or Cobol because

    1. PL/I can do what both of those can.
    2. It is usually easier to say it in PL/I.
    3. PL/I has somewhat reasonable control structures (DO-WHILE and IF-THEN-ELSE).
    4. PL/I doesn't have a vast number of silly restrictions.
    5. If you ever thought you wanted it, it is likely to be in PL/I.

Points 4 and 5 and the committee approach to language design have resulted in a language which is well nigh impossible to compile. (The list of still-born PL/I compilers is large and growing, and the bugs in and size of existing PL/I compilers attest to this fact.) This difficulty (and expense) in compiling results in the generation of a fair amount of horrible code. The pell-mell removal of silly restrictions (Point 4 above) has swung the pendulum so that PL/I allows multitudinous silly generalizations. For example, in PL/I you are given the option of using a negative binary scaling factor. (I realize you probably do not know or care what one of those might be, and that is exactly the point.)

            Despite these problems (or actually because of them as I will explain) the use of PL/I is increasing. More businesses are using it, more scientific calculations are using it, and more universities are teaching it.

            You catch minnows by placing a jar with a big hole on the outside and a little hole on the inside (like a funnel) into a stream. Swimming happily along, a minnow cogitates the advantages of jar-insidedness versus the more traditional jar-outsidedness he is used to. He learns a small part of PL/I, discovers it is better

 

Cartoon of fish in trap.

 

than, say, Cobol, and swims easily into the jar by converting his first program to PL/I. He soon discovers that PL/I is a large (read vast) language full of thousands of new, intriguing (and often uncompilable) features. These he uses at his leisure. If he turns to leave his jar, he can not easily get himself out through the small hole. The more he partakes of the disparate tasty PL/I features, the more firmly he becomes UNSPECed, ABNORMALed and GENERICed into the jar.

            The construction of this jar (a full blown PL/I compiler) is a gargantuan task which can be successfully carried off only by a financial behemoth. Said task has been accomplished by the obvious behemoth. It is well nigh impossible to build a full PL/I compiler to run on a mini computer, e.g., a PDP/11. It would probably be financially disastrous for a mere software house to attempt to bring to life a full blown PL/I compiler for any computer. Even large (as opposed to behemoth) computer manufacturers may not succeed in such an undertaking.

            Let me make one thing perfectly clear [sic], and that is that it is not difficult to write a good compiler for a convenient, powerful and sophisticated language. A reasonable Pascal compiled can be generated in three months. On the other hand, Pascal does not smell with a trunk, hop, or flap a dove's wings; it is not a committee language.

            The advantages of PL/I to a computer manufacturing behemoth are obvious:

  • The compiler uses a lot of computer time.
  • The generated code uses a lot of computer time and memory.
  • The compiler can be implemented and maintained only by the behemoth, and hence is useable only in the behemoth's machines.

            It would seem that only a carefully organized intensive effort can tame (or dismantle) the PL/I language. This effort could arise from the universities, as they tend to be well informed, unbiased, and forward looking. They could, for example, propose a simplified, understandable, but "complete" subset of PL/I which they might embarrass manufacturers into accepting. Unfortunately, academics can never agree, so that will not work.

            Similarly, this job (taming PL/I) could be (and is being) under taken by standards committees in, say, SHARE. Again, I do not expect (but hope for) the necessary effort to arise from such committees. Alas, the U.S.A. government could decree that its manufacturers follow a reasonable "standard" for PL/I----that will not work because the U.S.A. government has such a hard time being reasonable.

            Any committee on PL/I will probably spend a significant portion of its time calculating the mean and standard deviation of the individual member's favourite locations for semi-colons, and will probably not produce a reasonable recommendation on PL/I (which would not be enforced anyway).

            The best people in programming language design will have nothing to do with PL/I; they will refuse to teach the uglinesses of PL/I to their handful of followers in the vain hope that the language will die because of their neglect.

3. Some Vain Suggestions

            We must teach structured programming. The structured programming approach is just plain superior to encouraging students to spend their time inventing and; glorying in micro tricks to save micro seconds. Students must be taught how to break large programming problems cleanly into smaller ones. This means eliminating (or constraining) mechanisms such as PL/I's GOTOs and pointers.

            We should teach precision programming. That is, a student must be taught that a good program is one which is correct before it is run. The only way for a student (or anyone else) to produce good programs is to know his programming language (or language subset) intimately. A student must be able to master a "complete" language (or complete language subset) so that he can concentrate on producing computations. It must not be forgotten that teaching programming should not revolve around the trivia of redundant language features. Rather it should centre upon development of skills for producing correct and reasonably efficient computations with a reasonable expenditure of programmer time.

            It is the rare human who is aware of all the features of PL/I. Thus, a programmer can make himself indispensable by using a significant portion of the plethora of PL/I features in his programs. As G. Weinberg points out in his book "The Psychology of Computer Programming", in dispensable programmers should immediately be fired!

            Redundancy may be unpopular with mathematicians but is vital for astronauts and programmers. Avoiding declarations (and using certain defaults) in PL/I removes an important source of spelling and consistency checks. Automatic conversion of certain types removes the redundancy implied by explicit conversion For example, the loop:

                        DO I = O, I = l;

                                    statements

                        END;

will be executed twice, each time with I set to O. The reason is that it is equivalent to

          DO I = O,O;

               statements

          END;

because "I = 1" is a Boolean expression with a value of false (bit string 'O'B in PL/I speak) which is converted to O and assigned to I. From the foregoing paragraphs, we can draw some conclusions about teaching PL/I.

Conclusion 1: We should not teach (or use) all of PL/I. Indeed, we probably are should discipline ourselves and our students to use a sufficient and small subset of the features of PL/I.

Conclusion 2: We should not be lulled into using PL/I defaults, automatic conversion, etc., which remove redundancy or clarity. This means that not only should we rule out the use of certain PL/I features, but that we should insist upon using certain features. For example, we should insist upon declaring all variables, and should accept only the most obvious default attributes, such as REAL as opposed to COMPLEX. (In PL/I, FLOAT means what Fortran means by REAL, and REAL means not COMPLEX.)

            It is not wise to give a bright student a PL/I reference manual and tell him to learn the language. Nor is it wise to teach him subset of the language and leave him with the impression that the way to improve his programming talents is to learn more and more PL/I features. Rather, to save the student from the minnow jar's deceptions and temptations, a student will need firm guidance.

            It is not presumptuous of us to require that students (and employees) use a strict and small subset of PL/I. It is simply good sense.

 

4. Some Detailed Rules

            In teaching introductory programming using PL/I I have developed a number of simplifying rules which subset the PL/I language. These rules eliminate nearly all of the PL/I precision and conversion disasters (examples of these disasters are given above). These also eliminate many unnecessary features and encourage the construction of understandable, well structured programs. Here is a list of some of these rules.

1. Use no abbreviations. (Even when writing on the board.) Remember, in any program of reasonable size, the effort saved by omitting EDURE is negligible compared to the effort of making the program correct. Besides, why memorize that DCL, but not DEC, means DECLARE when DECLARE obviously does.

2. Declare all variables and declare them at the beginning of a block (as in Algol). This creates welcome redundancy and localizes the specifications of data.

3. Use no GOTO statements. Students will not miss them if you do not miss them. Concentrate instead on DO-WHILE and IF-THEN ELSE. (Very sadly, PL/I does not have a case statement.)

4. Use only a simple form of iterative DO. The two forms "DO WHILE (expression);" and "DO variable=initial TO final [BY step];" are good choices.

5. Do not use the fixed point division operator. The division of two fixed point numbers (using the operator /) yields very fishy results which expose some of the disasters of the PL/I precision rules. These disasters can be avoided by using the built-in function DIVIDE. For example, instead of 1/3 use DIVIDE (1,3,8,5) which yields eight significant figures with five to the right of the decimal point. (Floating point division is OK.)

6. Do not use the-exponentiation operator (**). The PI./I definition of this operator takes pages to explain and has weird properties. For example, I expect you would be surprised to discover that

.1**1 and .1**+1

do not yield the same answers. (Use multiplication or logs and antilogs to avoid exponentiation.)

7. Do not use the BINARY attribute. (Use only DECIMAL numbers. (You would never consider using the ugly PL/I BINARY constants.) Of course, DECIMAL is a misnomer since ;n PL/C, for example, all numbers are represented internally in binary form. This rule is a little difficult to defend in that in most implementations, BINARY FIXED storage and arithmetic is cheaper than that of DECIMAL FIXED. However, this restriction eliminates many number conversion considerations while still leaving a "complete" language subset. This rule also eliminates consideration of the mystery of scaling factors for binary numbers.

8. Do not use negative scaling factors. You probably would not use them anyway, but the point is, do not teach negative scaling factors just because they exist.

9. Use bit strings only of length one. Bit strings of length greater than one have some uses, but the clumsiness and silly generality of PL/I bit strings should be avoided. Instead, simply consider 'O'B and 'l'B to mean false and true, and restrict the attribute BIT(number) to BIT(l). Do not use string operations on bit strings. In brief, restrict bit strings to act like Booleans.

10. Do not use DATA directed input/output. List directed input/output should be taught because it provides (a) an easy way to read and print variables, (b) an easy way to print constants and expressions, including constant character strings which are titles, and (c) a convenient and efficient way to read or print quantities of data. DATA directed input/output provides a convenient way to read and print variables, but does not provide advantages (b) and (c). Hence, the student should not be burdened with the extra weight of DATA directed input/output. (EDIT directed input/output, with a small number of format options, should be taught.)

11. Use no implicit conversion between strings and numbers. (This is not allowed in PL/C.) Instead, perform all such con versions by GET STRING and PUT GET and PUT must be learned to do input/output, no additional conversion rules need be mastered.

12. Use no ON conditions. The occurrence of a disaster, such as a division by zero, usually leaves no alternative but to print some indication of the existing state of the computation (variable values and numbers of recently executed statements.) A reasonable teaching system such as PL/C or PLUTO will accomplish this automatically. Generally, ON conditions generate unruly and surprising transfers of control and tend to destroy program structure. The ON ENDFILE condition may be justifiable (because PL/I fails to set a testable end-of-file flag) in the following form:

ON ENDFILE(SYSIN) END_OF_CARDS='l'B;

13. Strictly adhere to a set of paragraphing rules. IF statements should have consistent indentations of the THEN and ELSE clauses. Similarly, all blocks (procedures, do groups, and begin blocks) should be consistently indented. Comments should be indented to the current indentation level of the program. It is good practice to close (with */) a long comment at the end of each line and to reopen it on each new line. Paragraphing rules make a program easier to read, maintain and understand.

            Of course, it would be much easier to teach PL/1 if there were a compiler which enforced the restrictions used by the teacher. (One of the author's students is building one.) Indeed, the gull ability of PL/I compilers in accepting poor programming style is a deterrent to teaching.

            (These rules have been developed to improve programming. A person in compiler-writing will recognize that incorporation of these rules into PL/I greatly eases the task of compilation. The conclusion seems obvious, but... it is too much to hope for.)

5. Conclusion

            The author has used the rules he suggests in teaching PL/I. Sticking to such a subset of PL/I requires considerable discipline. It is amusing how one'-s colleagues and students (and even one's self) tend to believe the compiler's simple evaluation of a program (e.g., NO ERRORS DETECTED). One has only to consider the value of reliable, maintainable, readable programs to see that the compiler's evaluation, while honest, is much too lenient. Many things which are not errors to a PL/I compiler are errors in programming style.

            Imposition of these rules makes teaching, learning and using PL/I easier. The student is left with fewer choices (many of the choices in full PL/I are bad or equivalent). Writing any program consists of a sequence of decisions about how to express an algorithm. Pre-selection of a class of reasonable choices can not help but ease the programming task. When using these rules, student solutions to programming assignments are interestingly consistent and understandable.

            Removing some of the "richness" of PL/I from a student (or anyone else) does not rob him of his chance for creativity in programming. Rather, it leaves him freer to concentrate his creativity upon producing a computation. Programming remains an art. This paper is a plea for the use of effective, proven techniques in the art of programming.

Acknowledgements

            The ideas about complete language subsets have arisen from discussions with J. Horning, D. Wortman, and E. Economou. H. Mills coined (I think) the term "precision programming". Articles by E. Dijkstra, N. Wirth, and T. Hoare have stated well the case for structured programming. Finally, this paper would not have been possible without the patient encouragement and harassment of hundreds of students and several PL/I compilers.