Classics: Stanford University
Classics: Stanford University
Classics: Stanford University
Today I want to talk about the paradigms of programming, how they affect our success
as designers of computer programs, how they should be taught, and how they should be
embodied in our programming languages.
In the first phase, that of top-down design, or stepwise refinement, the problem is
decomposed into a very small number of simpler subproblems. In programming the
solution of simultaneous linear equations, say, the first level of decomposition would be
into a stage of triangularizing the equations and a following stage of back-substitution
in the triangularized system. This gradual decomposition is continued until the
subproblems that arise are simple enough to cope with directly. In the simultaneous
equation example, the back substitution process would be further decomposed as a
backwards iteration of a process which finds and stores the value of the ith variable from
the ith equation. Yet further decomposition would yield a fully detailed algorithm.
The second phase of the structured programming paradigm entails working upward
from the concrete objects and functions of the underlying machine to the more abstract
objects and functions used throughout the modules produced by the top-down design.
In the linear equation example, if the coefficients of the equations are rational functions
of one variable, we might first design a multiple-precision arithmetic representation
Robert W Floyd, Turing Award Lecture, 1978: “The Paradigms of Programming,” Communications of the
ACM, Vol.22:8, August 1979, pp.455-460. ©1979 Association for Computing Machinery, Inc. Reprinted by
permission.
and procedures, then, building upon them, a polynomial representation with its own
arithmetic procedures, etc. This approach is referred to as the method of levels of
abstraction, or of information hiding.
I believe that the current state of the art of computer programming reflects inadequacies
in our stock of paradigms, in our knowledge of existing paradigms, in the way we teach
programming paradigms, and in the way our programming languages support, or fail to
support, the paradigms of their user communities.
The state of the art of computer programming was recently referred to by Robert Balzer
[3] in these words: “It is well known that software is in a depressed state. It is unreliable,
delivered late, unresponsive to change, inefficient, and expensive. Furthermore, since
it is currently labor intensive, the situation will further deteriorate as demand increases
and labor costs rise.” If this sounds like the famous “software crisis” of a decade or so
ago, the fact that we have been in the same state for ten or fifteen years suggests that
“software depression” is a more apt term.
Thomas S Kuhn, in The Structure of Scientific Revolutions [16], has described the
scientific revolutions of the past several centuries as arising from changes in the
dominant paradigms. Some of Kuhn’s observations seem appropriate to our field. Of
the scientific textbooks which present the current scientific knowledge to students,
Kuhn writes:
Those texts have, for example, often seemed to imply that the content of
science is uniquely exemplified by the observations, laws and theories
described in their pages.
In the same way, most texts on computer programming imply that the content of
programming is the knowledge of the algorithms and language definitions described in
their pages.
The study of paradigms, including many that are far more specialized than
those named illustratively above, is what mainly prepares the student for
membership in the particular scientific community with which he will
later practice. Because he there joins men who learned the bases of their
field from the same concrete models, his subsequent practice will seldom
evoke overt disagreement over fundamentals...
In computer science, one sees several such communities, each speaking its own
language and using its own paradigms. In fact, programming languages typically
encourage use of some paradigms and discourage others. There are well defined schools
of Lisp programming, APL programming, Algol programming, and so on. Some regard
data flow, and some control flow, as the primary structural information about a
program. Recursion and iteration, copying and sharing of data structures, call by name
and call by value, all have adherents.
In computing, there is no mechanism for reading such men out of the profession. I
suspect they mainly become managers of software development.
Balzer, in his jeremiad against the state of software construction, went on to prophesy
that automatic programming will rescue us. I wish success to automatic programmers,
but until they clean the stables, our best hope is to improve our own capabilities. I
believe the best chance we have to improve the general practice of programming is to
attend to our paradigms.
solves a problem for given input by first iteratively solving it for all smaller inputs.
Cocke’s algorithm successively found all parsings of all substrings of the input. In this
conceptual frame, the problem became nearly trivial. The resulting algorithm was the
first to uniformly run in polynomial time.
At around the same time, after several incorrect top-down parsers had been published,
I attacked the problem of designing a correct one by inventing the paradigm of finding
a hierarchical organization of processors, akin to a human organization of employers
hiring and discharging subordinates, that could solve the problem, and then simulating
the behavior of this organization [8]. Simulation of such multiple recursive processes
led me to the use of recursive coroutines as a control structure. I later found that other
programmers with difficult combinatorial problems, for example Gelernter with his
geometry-theorem proving machine [10], had apparently invented the same control
structure.
John Cocke’s experience and mine illustrate the likelihood that continued advance in
programming will require the continuing invention, elaboration, and communication
of new paradigms.
If the advancement of the general art of programming requires the continuing inven-
tion and elaboration of paradigms, advancement of the art of the individual program-
mer requires that he expand his repertory of paradigms. In my own experience of
Contact with programming written under alien conventions may help. Visiting MIT on
sabbatical this year, I have seen numerous examples of the programming power which
Lisp programmers obtain from having a single data structure, which is also used as a
uniform syntactic structure for all the functions and operations which appear in
programs, with the capability to manipulate programs as data. Although my own
previous enthusiasm has been for syntactically rich languages like the Algol family, I
now see clearly and concretely the force of Minsky’s 1970 Turing lecture [19], in which
he argued that Lisp’s uniformity of structure and power of self reference gave the
programmer capabilities whose content was well worth the sacrifice of visual form. I
would like to arrive at some appropriate synthesis of these approaches.
It remains as true now as when I entered the computer field in 1956 that everyone wants
to design a new programming language. In the words written on the wall of a Stanford
University graduate student office, “I would rather write programs to help me write pro-
grams than write programs.” In evaluating each year’s crop of new programming
languages, it is helpful to classify them by the extent to which they permit and
encourage the use of effective programming paradigms. When we make our paradigms
explicit, we find that there are a vast number of them. Cordell Green [11] finds that the
mechanical generation of simple searching and sorting algorithms, such as merge
sorting and Quicksort, requires over a hundred rules, most of them probably paradigms
familiar to most programmers. Often our programming languages give us no help, or
even thwart us, in using even the familiar and low level paradigms. Some examples
follow.
W′= f(W, R)
R′ = g(W, R)
which give the numbers of wolves and rabbits at the end of a time period, as a function
of the numbers at the start of the period.
FOR I : – – – DO
BEGIN
W := f(W, R);
R := g(W, R)
END
where g is, erroneously, evaluated using the modified value of W. To make the program
work, we must write:
FOR I : – – – DO
BEGIN
REAL TEMP;
TEMP := f(W, R);
R := g(W, R);
W := TEMP
END
The beginner is correct to believe we should not have to do this. One of our most
common paradigms, as in the predator-prey simulation, is simultaneous assignment of
new values to the components of state vectors. Yet hardly any language has an operator
for simultaneous assignment. We must instead go through the mechanical, time-
wasting, and error-prone operation of introducing one or more temporary variables and
shunting the new values around through them.
Because both input and output are naturally expressed using multiple levels of iteration,
and because the input iterations do not nest with the output iterations, the problem is
surprisingly hard to program in most programming languages [14]. Novices take three
or four times as long with it as instructors expect, ending up either with an undisci-
plined mess or with a homemade control structure using explicit incrementations and
conditional execution to simulate some of the desired iterations.
When a language makes a paradigm convenient, I will say the language supports the
paradigm. When a language makes a paradigm feasible, but not convenient, I will say
the language weakly supports the paradigm. As the two previous examples illustrate,
most of our languages only weakly support simultaneous assignment, and do not
support coroutines at all, although the mechanisms required are much simpler and
more useful than, say, those for recursive call-by-name procedures, implemented in the
Algol family of languages seventeen years ago.
MAIN__PROGRAM:
BEGIN
TRIANGULARIZE;
BACK__SUBSTITUTE
END;
BACK__SUBSTITUTE:
FOR I := N STEP -1 UNTIL 1 DO
SOLVE__FOR__VARIABLE(I);
SOLVE__FOR__VARIABLE(I):
–––
–––
TRIANGULARIZE:
–––
–––
Procedures for multiple-precision arithmetic
Procedures for rational-function arithmetic
Declarations of arrays
In most current languages, one could not present the main program, procedures, and
data declarations in this order. Some preliminary human text-shuffling, of a sort readily
mechanizable, is usually required. Further, any variables used in more than one of the
multiple-precision procedures must be global to every part of the program where
multiple-precision arithmetic can be done, thereby allowing accidental modification,
contrary to the principle of information hiding. Finally, the detailed break-down of a
problem into a hierarchy of procedures typically results in very inefficient code, even
though most of the procedures, being called from only one place, could be efficiently
implemented by macroexpansion.
Now I want to talk about what we teach as computer programming. Part of our
unfortunate obsession with form over content, which Minsky deplored in his Turing
lecture [19], appears in our typical choices of what to teach. If I ask another professor
what he teaches in the introductory programming course, whether he answers proudly
“Pascal” or diffidently “FORTRAN,” I know that he is teaching a grammar, a set of
semantic rules, and some finished algorithms, leaving the students to discover, on their
own, some process of design. Even the texts based on the structured programming
paradigm, while giving direction at the highest level, what we might call the “story”
level of program design, often provide no help at intermediate levels, at what we might
call the “paragraph” level.
I believe it is possible to explicitly teach a set of systematic methods for all levels of
program design, and that students so trained have a large head start over those
conventionally taught entirely by the study of finished programs.
It also, on a higher level, instantiates the responsibilities of the programmer toward the
user of the program, including the idea that each component of a program should be
protected from input for which that component was not designed.
Howard Shrobe and other members of the Programmer’s Apprentice group [22] at MIT
have successfully taught their novice students a paradigm of broad utility, which they
call generate/filter/accumulate. The students learn to recognize many superficially
dissimilar problems as consisting of enumerating the elements of a set, filtering out a
subset, and accumulating some function of the elements in the subset. The MACLISP
language [18], used by the students, supports the paradigm; the students provide only
the generator, the filter, and the accumulator.
where I have circled the parts of each summand that are useful in computing the next
one on the right. Without describing the entire design paradigm for such processes, a
part of the design of the state transition is systematically to find a way to get from
1. 3
Q= , C =5
2 5. 2 . 4
1 1 1. 3
S= + 3 + 5
2 2 .2 .3 2 .2 .4.5
to
1. 3 . 5
Q′ = 7
, C′ = 7
2 .2 . 4.6
1 1. 3 . 5
S′ = +…+ 7 .
2 2 .2 .4.6.7
The experienced programmer has internalized this step, and in all but the most complex
cases does it unconsciously. For the novice, seeing the paradigm explicitly enables him
to attack state-machine problems more complex than he could without aid, and, more
important, encourages him to identify other useful paradigms on his own.
To sum up, my message to the serious programmer is: spend a part of your working day
examining and refining your own methods. Even though programmers are always
struggling to meet some future or past dead-line, methodological abstraction is a wise
long term investment.
To the teacher of programming, even more, I say: identify the paradigms you use, as
fully as you can, then teach them explicitly. They will serve your students when Fortran
has replaced Latin and Sanskrit as the archetypal dead language.
To the designer of programming languages, I say: unless you can support the paradigms
I use when I program, or at least support my extending your language into one that does
support my programming methods, I don’t need your shiny new languages; like an old
car or house, the old language has limitations I have learned to live with. To persuade
me of the merit of your language, you must show me how to construct programs in it. I
don’t want to discourage the design of new languages; I want to encourage the language
designer to become a serious student of the details of the design process.
Thank you, members of the ACM, for naming me to the company of the distinguished
men who are my predecessors as Turing lecturers. No one reaches this position without
help. I owe debts of gratitude to many, but especially to four men: to Ben Mittman, who
early in my career helped and encouraged me to pursue the scientific and scholarly side
of my interest in computing; to Herb Simon, our profession’s Renaissance man, whose
conversation is an education; to the late George Forsythe, who provided me with a
paradigm for the teaching of computing; and to my colleague Donald Knuth, who sets
a distinguished example of intellectual integrity. I have also been fortunate in having
many superb graduate students from whom I think I have learned as much as I have
taught them.
References
[1] Aho, A.V., Hopcroft, J.E., and Ullman, J.D. The Design and Analysis of Computer Algorithms. Addison-
Wesley, Reading, Mass. 1974.
[2] Aho, A.V., and Ullman, J.D. The Theory of Parsing, Translation, and Compiling, Vol. 1: Parsing. Prentice-
Hall, Englewood Cliffs, New Jersey, 1972.
[3] Balzer, R. Imprecise program specification. Report ISI/RR-75- 36, Inform. Sciences Inst., Dec. 1975.
[4] Conway, M.E. Design of a separable transition-diagram compiler. Comm. ACM 6, 7 (July 1963), 396-408.
[5] Davis, R. Interactive transfer of expertise: acquisition of new inference rules. Proc. Int. Joint Conf. on Artif.
Intell., MIT, Cambridge, Mass., August 1977, pp. 321-328.
[6] Dijkstra, E.W. Notes on structured programming. In Structured Programming, O.J. Dahl, E.W. Dijkstra, and
C.A.R. Hoare, Academic Press, New York, 1972, pp. 1-82.
[7] Donzeau-Gouge, V., Huet, G., Kahn, G., Lang, B., and Levy, J.J. A structure oriented program editor: A first
step towards computer assisted programming. Res. Rep. 114, IRIA, Paris, April 1975.
[8] Floyd, R.W. The syntax of programming languages–a survey. IEEE EC-13, 4 (Aug. 1964), 346-353.
[9] Floyd, R.W. Nondeterministic algorithms. J.ACM 14, 4 (Oct. 1967), 636-644.
[10] Gelernter. Realization of a geometry-theorem proving machine. In Computers and Thought, E. Feigenbaum
and J. Feldman, Eds., McGraw-Hill, New York, 1963, pp. 134-152.
[11] Green, C.C., and Barstow, D. On program synthesis knowledge. Artif. lntell. 10, 3 (June 1978), 241-279.
[12] Hewitt, C. PLANNER: A language for proving theorems in robots. Proc. Int. Joint Conf. on Artif. Intell.,
Washington, D.C., 1969.
[13] Hewitt, C. Description and theoretical analysis (using schemata) of PLANNER... AI TR-258, MIT,
Cambridge, Mass., April 1972.
[14] Hoare, C.A.R. Communicating sequential processes. Comm. ACM 21, 8 (Aug. 1978), 666-677.
[15] Jensen, K., and Wirth, N. Pascal User Manual and Report. Springer-Verlag, New York, 1978.
[16] Kuhn, T.S. The Structure of Scientific Revolutions. Univ. of Chicago Press, Chicago, Ill., 1970.
[17] Lawler, E., and Wood, D. Branch and bound methods: A survey. Operations Res. 14, 4 (July-Aug. 1966), 699-
719.
From: Internet