Do Programmers Need Seat Belts?

by Richard P. Gabriel

(Originally Printed in ACM SIGPLAN Debates as a Counterpoint)

Once upon a time there were two inventors, each trying to invent a personal flying machine. One of them, Mr Rabbit, thought the most important thing about a personal flying machine was how fast it could fly, because people would buy it only because it could fly them places quickly. The other, Mr Bear, felt that it was most important for the pilot to feel comfortable while flying.

Mr Rabbit's flying machine was fast, but it really was just a scud missile with a people capsule - launch and forget. It required telling an on-board computer before each flight the exact destination, and it would plot a course and launch. If something went wrong, the missile would crash or explode, but there were many safety features built in that would make sure the flight path was reasonable and that nothing would happen. When the missile crashed it was hard to repair and get going again. The cockpit was almost an afterthought, and the pilot was really just a passenger strapped into a coffin-like box the whole trip. But it was very fast..

Mr Bear's airplane was a funky push-propeller plane with a canard. It flew easily and was responsive to the pilot, who sat in a body-molded seat with numerous and effective safety belts. The instruments were easy to see and use, and you could take off and land on a very short runway. The controls were responsive but wouldn't overcorrect, and so the airplane was forgiving. It flew a little more slowly than Mr Rabbit's, but as time passed, the difference in speed wasn't noticeable unless you were going extreme distances, like around the world a few times.

When computers were new-fangled and programmers were few and far between, it was a miracle to think that you could program in a language you could read and which expressed at anything like a high level what you wanted the computer to do. But the computer's memory was just a flat line of words or bytes, and the processor was slow, not really much faster than a prodigy reckoning in his head. The obvious way to think about a high-level language was to carve up memory into small parcels of bits which were to be interpreted a particular way - as floating point numbers, as characters, as strings - and to blindly manipulate those patterns of bits knowing (or assuming) they were what the operations thought they should be.

So people invented types as a way of telling the program that prepared the executable code what sorts of data there would be and how to manipulate it. The preparation program - the compiler and linker - would then imagine regions of memory carved up into chunks, and act like a blind robot making a cake: Just as the robot might reach to the right 130 degrees and 10.7 centimeters down and grab, assuming it was picking up a pastry bag, the CPU might reach into location 31425 and grab, assuming it was the 56th element of a vector - and a floating-point number to boot.

The thought these pioneers had for how programmers should work was this: Each programmer would say: "Please, God, just put my program together and run it as fast as You can and I will never eat a jelly doughnut again!"

But not everyone thought this way. Some people thought you should program by conversing with the computer, typing in commands or expressions and seeing how the computer responded. Programmers would define operations and procedures and try them out, testing as they went, incrementally working, coaxing fast turnaround from the slow machines by piecing together the parts and checking them in small test cases, working up to the final result.

The way these program preparation programs worked was to define the way that memory was divided into meaningful things - objects? - so that when a program picked up something, it could tell what it was. It's like the cakemaking robot being able to tell whether it has picked up the pastry bag.

Neither camp thought it was a good idea to add 64 to the character "a" unless it was in the middle of the most hidden, performance-crazy part of a system. In fact, both camps approached the idea of strong typing in their own ways

It's useful to distinguish between strongly, statically, and dynamically typed languages. A strongly typed language is one where operations are allowed only on data of the proper types; a statically typed one does its typechecking at compile-time; and a dynamically typed one does its typechecking at runtime (and adds exception handling to the mixture). The best large-lump development languages are strongly, statically typed, and the best incremental development languages are strongly, dynamically, and, sometimes, statically typed.

The large-lump development people decided that they would not allow programs to be constructed by the compilation system unless it could be shown that every operation was performed on data of the proper types. The incremental development people decided that they would not allow the execution of operations on data of improper types and would make their runtime systems signal an error if such an attempt were made.

One of the claims of the large-lump development crowd is that it is easier or faster to catch certain errors at compile time. I have used both kinds of languages and I find I spend about the same amount of time dealing with type errors in both kinds of language. In the days when compilers would bomb out after the first one or two errors, it would sometimes take a lot longer to find the type errors in the compiled language. Of course, there are no such compilers today.

The main reason, though, that type errors take about the same time to deal with is that they are not always merely the result of typos or simply fixed problems; often a type error would signal a logical, algorithmic, design, or even architectural mistake. In my experience, dealing with type errors per se was a negligible part of the problem of developing a realistic system.

One of the benefits of dynamic typing is that the types of objects can be determined at runtime, by application logic. This enables what was once called data-driven programming. Originally this was supported by a freewheeling style and was chaotic as anything was in the early 1960's. Later, though, discipline and precision were added to the idea and data-driven programming evolved into object-oriented programming.

But what happened in the evolution of programming languages is that the large-lump folks didn't realize that large-lump development as a system-building methodology was hard to do for software organizations, or that building a system was not yet like building a highway or bridge. On the other hand, the incremental development folks didn't realize that the performance lost in checking each operation would be enough to sway almost everyone away from using such languages so that today the disparity between the sizes of the communities is huge.

You cannot fault the large-lump philosophy for having won, because when computers were horrendously expensive and people incredibly cheap, the computer's time was more valuable than a person's time. Today, though, it isn't clear how the tradeoff - if made afresh - would turn out. Even PC-class computers are faster by a large margin than is needed for the most common computing tasks, and only in scientific, engineering, and graphics (including multimedia) is the issue of CPU performance paramount. One could argue that the problem of performance today centers more on network - especially Internet- bandwidth.

Nevertheless, the tradeoff cannot be made afresh because we have several generations of computer-savvy folks who have learned the received wisdom that the speed and size of a running system is the most important thing in thinking about a computer language (or system), and ease of incremental development is not. In fact, the entire programming methodology business, er, discipline is based on the idea that development is done in accordance with the way software is prepared: as large-lump development.

Though the large-lump development folks claim that type safety is the reason that statically typed languages were developed, I don't believe it for a minute. Early Fortran compilers weren't very typesafe and represented the pinnacle of performance. In the mid-1960's I worked as a scientific programmer, and the only thing I and my colleagues were concerned about was speed, performance, and efficiency. Why, we even did arithmetic on characters - in Fortran - if we had to.

Today some companies and research labs are trying to figure out ways of making it possible to do incremental development in a language and language-preparation system that assumes large-lump development. In some cases, notably C++, it is very difficult to achieve good results unless the underlying implementation philosophy is changed to that of languages like Lisp or Smalltalk. However, this makes it difficult to mix code from different compilers and frequently requires providing all the standard libraries as prepared by the alternative language system - not always possible unless you manufacture the operating system.

The interesting case is Java. Java is a language based on a neat dose of incredibly innovative uses of existing ideas and terminology. Java is basically a cleaned up C++ (with all the problems of pointer arithmetic and multiple inheritance eliminated), implemented exactly as Lisp and Smalltalk have been, but used for Web/Internet programming. The intellectual heritage of Java includes C++, Lisp, and Smalltalk, and also Postscript and News. The latter two are mechanisms for sending a procedural representation of a picture rather than a declarative one: that is, a program is sent and executed remotely, and not only can this require fewer bits to be sent than with a bitmap, but the program could perform animation.

What has been done most brilliantly, though, is to rename some of the components so as to render them fresh and catchy, for example: Applets (small programs that are the equivalent of Postscript in the analogy), just-in-time compiler (the technology to translate virtual machine codes into native code which can be cached for faster execution and which has been in common use in the Smalltalk world for at least 5 years), and Java (a fresh name for a cleaned up C++).

Java uses incremental development implementation techniques to achieve the claimed benefits of safe, small code, but Java is couched in the context of the Internet where the slower execution speed of incremental development languages is insignificant compared to the network latency problem. It will be interesting to see whether the learned kneejerk response to so-called interpreted languages ("so cute, but so slow") applies to Java as a programming language, overwhelming the magic of the new context and the cute names.

I believe there are very few fundamental differences between the two factions anymore except for obsession with speed. Java is the most recent experiment with trying to unify the two camps. Let's hope.