Towards Academics and Engineers Talking Usefully About Programming Languages
Daniel S. Wilkerson; 19 Oct 2005 and 7-8 June 2006
I had a pretty iconic conversation today with an academic at OSQ, the research group at Cal Berkeley Division of Computer Science on Programming Languages. In the small the conversation was about C++, but in the large it was about the apparent dichotomy between the academic and engineering communities and their fundamental aesthetic regarding programming languages. Being in both worlds I thought it might help to write down what I see as the basic problem that prevents the communities from communicating.
The academic/analysis community is very concerned with assigning a bottom-up semantics to programs in the mathematical tradition where everything is fully specified, at the bottom, axiomatically. Proofs and absolute correctness are the aesthetic. They feel that the engineers don't listen when they say how bad it is to programming in a language where the semantics of are not fully mathematically specified. What happens if you fall into a semantic hole by writing a program that does not have well-defined semantics?
The engineering/synthesis community is concerned with problems that I think academics sometimes don't know about: large real-world programs, measured in units of tens of thousands of lines of code, exhibit properties that are fundamentally different from those of smaller programs. It is like the fact that the laws of physics just do not scale linearly: if you understand atoms, it does not mean you understand biology or humans just because they are made of atoms; different phenomena occur at different scales. Learning to program in C is completely different from being a competent designer of Object Oriented programs.
The primary concern of senior software engineers working on large programs is how to appropriately factor the code: how to obtain large complex behavior as a product of simpler and independent modules. Done right, factoring effectively takes the logarithm of the complexity of a problem, as the results are a product of that of the modules whereas the difficulty is only a sum. Obtaining a well-factored program is the major concern for most engineers and seems completely unaddressed by the programming languages community; until very recently I had never heard the word "re-factor" in any computer science class ever. Factoring works, but no one knows how it is done exactly; it is just a subtle emergent fact about engineering that it is possible and effective.
This would suggest another kind of study of programming languages: start from what works and try to figure out what about it works, rather than insisting on starting at formal methods just for the sake of formality. That is, even if the means by which humans are able to build software is not understood, the fact that they can and do is the appropriate the starting point for a truly scientific study of the act of constructing software. The evident lack of appreciation for this concern by academics I think is the major reason that engineers don't listen to what academics have to say: academics have not understood the primary concern that engineers have. In engineering effective construction of working software and human understanding is the aesthetic.
But what of the question posed by academics mentioned above: what happens if one writes a nonsense program? As Professor Alex Aiken points out, there is a semantic gap between the abstractions that the language provides and those at which the programmer programs. A programmer must close this gap to get a program written and if the gap is far enough, he is basically inventing another special-purpose language in between: for example C programmers often reinvent vtables (using structs of function pointers) and templates (using the preprocessor). The data starts to become Turing complete. In fact "interpreter" is one of the standard Design Patterns. One can see how horrible configuration file languages get because the program has evolved into an interpreter without being designed as one. At this point the fact that the low level language is completely well-specified becomes irrelevant because it is too "far away" from the abstractions that the programmer is thinking in anyway; he might as well just use assembly language as his well-specified low-level language. Many C and C++ programmers do just that.
Therefore it is not so relevant for the engineer that C++ may have bad semantics in various corner conditions. What is relevant to him is that C++ does have sufficiently powerful semantics such that the gap from his program to the language is not too far. He can learn to avoid the bad corners by using known idioms. It is a library of useful idioms that a programmer can use to express his program that is more important than the lack of semantic holes. It is a rich coverage of the semantic space while tolerating nonsense corners that is the choice most industrial programmers are making, over a language that is well specified but "flat" or lacking in expressive power.
For many years I wondered why it was even possible to say nonsense in natural language, such as English; after all, evolution does not have time to waste and life has been serious business for many millennia; why would a language containing nonsense sentences arise? Aren't they a waste at best and dangerous at worst? Nonsense persists because orthogonality is the optimal solution to the mapping-language-to-reality problem and linguistic power results in nonsense corners; they must simply be tolerated. Larry Wall has more to say about this in his amazing essay "Perl, the first postmodern computer language".
Perhaps someday we can have effective software construction in a mathematically rigorous language. Until academics understand why C++ is so popular I am not hopeful.