What Makes a Good Programming Language?
What Is a Programming Language?
Programming languages, like spoken languages, are ways of communicating ideas. When two people who know the same language talk, they’re able to understand each other because they both know the rules that formalize how to translate sounds into meaning and vice versa.
Computers don’t understand human languages. Worse, the languages they do understand—their instruction sets—don’t mesh very well with how most humans speak. Imagine for a moment a French person talking to a Chinese person. Neither of them understands the other’s native language, but if they speak a common second language, such as English, they can still talk to each other. This is more or less the situation when it comes to programming languages.
The English skills of the speakers in that last example also have direct parallels in terms of programming languages. When you have one person speaking and another person listening, comprehension depends on two things:
- The speaker’s ability to translate ideas into the language
- The listener’s ability to translate the spoken words into ideas
The speaker’s ability is equivalent to the programmer’s skill, and the listener’s ability is equivalent to the compiler’s efficiency.
A programming language is a compromise. Translating a language such as English directly into a machine language is very difficult for a machine. Similarly, "speaking" a machine language well is very difficult for a human. A programming language is one that a human can speak reasonably well, and that a computer can translate into a language that it understands.
Language, Framework, and Runtime
Most languages are very small; for example, C contains only about 20 keywords. They control the flow of a program, but do little else. Writing a useful program using just the C language is almost impossible. Fortunately, the designers recognized this problem. The C language specification also defines some functions that must be available for C programs to call. The majority of these functions, known together as the C Standard Library, are themselves written in C, although a few primitive ones may need to be written in some language that’s yet more primitive.
Most languages have some kind of equivalent to the C Standard Library. Java, for example, has an enormous catalogue of classes in the java.* namespace that must be present for an implementation of the language to be called "Java." Twenty years ago, Common Lisp had a similarly large collection of functions. In some cases, the language is most commonly used with a collection of libraries, usually referred to as a framework, that are specified separately from the language itself.
Sometimes the line between language and framework is blurry. It’s clear that C++ and the Microsoft Foundation Classes are separate entities, for example, but for Objective-C the distinction is less clear. The Objective-C specification defines very little by way of a standard library. (Although, because Objective-C is a proper superset of C, the C Standard Library is always there.) Objective-C is almost always used in conjunction with the OpenStep API; these days, the common implementations are Apple’s Cocoa and GNUstep.
When you start adding libraries to a language, you lose some of the clarity of what makes the language unique. Writing an OpenGL program in Java has a lot more in common with writing an OpenGL program in C than it does with writing a Swing application in Java.
Another source of confusion is the runtime system. Languages such as Smalltalk and Lisp were traditionally run in a virtual machine (although most current Lisp implementations are compiled into native machine code). This requirement can lead people to perceive a given language as slow. Interpreted code is almost always slower than compiled code. This doesn’t have anything to do with the language, but it is important. Code run in a Lisp interpreter will be much slower than compiled Lisp code.
When judging a language, it’s important to differentiate between characteristics of a language and characteristics of an implementation of the language. Early implementations of Java were very slow; the just-in-time compiler was little better than an interpreter. This led to people calling Java an "interpreted language." The GNU project’s Java compiler destroyed this myth, although other design decisions in the Java language still prevent it from being run as fast as some other languages.