Teaching the Fatal Disease
(or)
Introductory Computer Programming Using PL/I
Richard
C. Holt
Dept.
of Computer Science and Computer Systems Research Group,
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
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:
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
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:
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
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.