Aphorisms on programming language design
It is easier to say what not to do, than to do what is needed. So do not be discouraged by this list; you, too, have something to contribute.
Every decision that matters is a tradeoff. There never was, and never will be, a perfect language. As humans have many purposes, so must programming languages.
The measure of a language is not what is possible in it, but what it makes easy.
Thus, a language’s strongest characteristic is what it makes automatic: What don’t you have to think about when using this language?
There is power in restraint. The less a language does, the more can be done to it.
Types are a tool of thought. They exist in the mind first, and in the machine second.
A statically typed language, therefore, guides your thinking. This is useful.
A dynamically typed language lets you think freely. This too is useful.Shorten the edit-compile-run cycle. Then shorten it some more.
Readability beats writeability. Or, predictability beats convenience.
It is better that code be transparent to read than convenient to write. This is less important for one-shot scripts, and more important for large codebases tended by many people.
Brevity is good, until it’s bad.
Brevity both helps and harms readability. You can only fit so much in your head at one time. Terse notation helps more fit in; it aids in reading and recognizing patterns. But it also steepens the learning curve and increases memory load.
Notations must be justified by frequency of use and power of thought gained.
A programming language is low level when its programs require attention to the irrelevant.
— Alan PerlisThis is, of course, a relative condition: Irrelevant to what? Irrelevant to whom? We often think of languages as being lower- or higher-level than other languages. Actually, they’re below or above certain concerns.
Explicitness is good, until it’s bad.
Explicitness is good when it prioritizes readability over writeability.
Explicitness is bad when it requires attention to the irrelevant.Can’t it do both? Of course it can!
Not everything is an object. Nor is everything a function, a string, a process, an actor, a value, a thunk, a message, a list, a file, or an expression. Not everything is data, nor is everything code. Not everything is lazy, immutable, pure, copyable, serializable, sortable, or transmissible over the network.
goto
is not the only control operator, and neither iscall/cc
.And yet, only by taking things to extremes do we truly learn their limits.
Yet long shots are worth taking.
The medium matters. Consider how the programmer will interact with your language or system. Will they use text files? a REPL? a shell? an IDE? a GUI? a spreadsheet? a structure editor? a browser? The language design should harmonize with the available methods of interaction. Indentation-sensitivity is a terrible fit for a line-oriented interactive shell, for example.
Syntax matters less than you think.
Syntax matters more than you think.
Syntax is a pain in the ass.
Performance is a lot like syntax.
“Declarative” means you can use it without knowing what it’s doing.
All too often, it means you can’t tell what it’s doing, either.
What applies to languages, applies to libraries. Consider, for example, how predictability beats convenience might apply to the design of a numerical library.
Performance matters. Syntax matters. Documentation matters. Libraries matter. Culture matters. First-mover advantage matters. Compatibility matters. Corporate support matters. Error messages matter.
But in the end, only two things are sure when it comes to language popularity:
To succeed, be the only available option on a popular platform.
Languages teach ways of approaching problems. Once you know the way, you don’t always need the language.
... but your friend might!
Before learning
$LANG
: read code, squash bugs.
After learning$LANG
: read code, squash bugs.