This is the third of a series of articles examining a 2009 interview with Bjarne Stroustrup. The first part is here with links to the other parts:
Abstraction
In ‘Masterminds of Programming’, Stroustrup is asked: ““Close to the hardware” seems to be a guiding principle in designing C++. Is it fair to say that C++ was designed more bottom-up than many languages, which are designed top-down, in the sense that they try to provide abstractly rational constructs and force the compiler to fit thes constructs to the available computing environment?”
Let’s just take that last bit. That is EXACTLY the job of the compiler to take the abstractions provided in the language and map those constructs to the target system. No forcing. That’s a compiler’s job, full stop. The compiler does this statically to produce an executable program. The operating system and runtime further do this for requests from a running program. The word ‘force’ seems used there to make this sound like a bad thing in a pejorative sense. A language specification defines the input to a compiler. You could say that forces the compiler to compile that language. If C++ is designed not to do that, where are we, what is a C++ compiler supposed to do?
It has been said that C++ was designed to make some sense out of any construct thrown at a compiler by a programmer. A good language specification gives a very precisely defined language for the compiler to accept. Thus the question is ill-considered.
Stroustrup: “I think top-down and bottom-up are the wrong way to characterise those design decisions. In the context of C++ and other languages, “close to the hardware” means that the model of computation is that of the computer — sequences of objects in memory and operations as defined on objects of fixed size — rather than some mathematical abstraction.”
Again it is the language’s role to define those abstractions and the compiler’s role to statically map logical abstract entities to the environment of the computer. The runtime and OS dynamically map those requests to the machine and its workload at the time. Programmers should not think about that. Programmers should concentrate on the nature of the data being processed and the appropriate abstract operations, that is the contents, not on the container.
Stroustrup really admits that C++ is a primitive low-level language, based on his low-level thinking. He has said he is a systems guy. The model of the machine is inherited from C. It is not a fixed thing for all machines.
https://queue.acm.org/detail.cfm?id=3212479
Alan Perlis (first recipient of the ACM Turing Award for computing): “A programming language is low level when its programs require attention to the irrelevant. While, yes, this definition applies to C, it does not capture what people desire in a low-level language.”
Thus C has its own computational model like any other languages. The problem is that is based on particular primitive machines of the past. As Perlis notes, that really is not close to hardware at all. C thus gives programmers the feeling they are doing ‘sleeves rolled up’, ‘serious’ programming (really coding), but they are not doing so. They are just thinking in low-level physical (yet not actually physical) terms rather than high-level abstract terms.
The programming profession must realise that it is high-level abstract programming based on abstract computational models that are machine independent that is the modern way to produce software. C++ has just perpetuated the low-level illusion of C, but this means that programmers really are dealing with irrelevant details for no good purpose at all. System programmers should deal with the real details of the platform, not the primitive computational model details of C.
Furthermore, sequences in memory and fixed-size operations are built into hardware because they themselves match the abstractions required in high-level programming.
Perhaps it is a failure of CS and programming education that when the structure of the machine is taught, students think they must think of this when programming. In fact, they should only know this in order to not think about how the program works in terms of the physical machine or “close to the hardware”. This should be made clear to students. Programs written should be expressed in the abstractions of the language.
Of course, the problem here is that when they think in terms of the machine they are then attracted to languages like C and C++ which do a poor job of abstracting the machine. This further traps programmers into the wrong way of thinking.
Stroustrup: “C++ differs from Java in that its underlying machine is the real machine rather than a single abstract machine.”
This is a little disingenuous. Java compiles code to an intermediate virtual machine code (JVM). It should not affect the language. JVM is just a target machine like any real machine. (JVM does have some restrictions in design — they never did generics right). However, like C is closely tied to DEC-style PDP architectures, perhaps Java is tied to the JVM, prompting Microsoft to invent its own ecosystem in C#.
Programmers don’t think in JVM intermediate code at all. Sure there is less ‘semantic gap’ between Java (and other languages like Scala) and the virtual machine code, but programmers still think in the problem-oriented terms of those languages. There is no assembler (as far as I know) for JVM (as it should be, all assembler should be a relic of the past). But many other languages which provide their own abstract computational model also generate code that executes directly on the ‘real’ machine — although these real machines can be very different. So which ‘real’ machine is C and C++ exposing?
If the semantic gap is to be beaten, we must not undermine the abstractions of the programming language.