Have modern programming languages failed? From the point of view of learnability and maintainability, yes! What would a truly maintainable and learnable programming language look like? This is the second of a six-part series exploring the future of programming languages (read The World’s Most Maintainable Programming Language: Part 1, The World’s Most Maintainable Programming Language: Part 3, The World’s Most Maintainable Programming Language: Part 4, The World’s Most Maintainable Programming Language: Part 5, and The World’s Most Maintainable Programing Language: Conclusion).
Consistency
The most important feature of a language is that it is completely consistent. There should be no inconsistency or nuance or shade of meaning.
Inconsistency is the enemy of understanding. This is true when discussing possible multiple interpretations of a construct during compilation and analysis or when reading the source code to a program.
Consistency is partly a notional problem. Consider languages that
support object orientation but force the programmer to name the invocant of
a method explicitly — there is tremendous potential for confusion! Is the
invocant this or self or object or
do all method and attribute accesses have an implicit invocant? How
unmaintainable the practice that raises such a question!
To solve this problem at least, while respecting the principle of
learnability and avoiding false cognates, a maintainable programming
language should name all invocants after the name of their classes. That
is, within a class named AlienInvader, all methods will
automatically have access to the invocant through the symbol named
alien_invader. (Consider how confusing it would be if the
language allowed unfettered creativity. Would there be instances such as
alien, predator, martian,
blob, horta, kudzu, and
poisson?)
That helps consistency in the small, but what about throughout an application or a problem domain?
Another potential point of inconsistency is in using different symbol
names for the same types of items. For example, a database handle may be
db or dbh or d or
handle. Various parts of the code may refer to the same type
of thing with different names — inconsistency that leads to
misunderstandings. This is a similar but different point. Here the problem
is the existence of separate pockets of jargon. When these pocket
communities overlap, their jargon conflicts. (As well, the term “handle” is
vastly inappropriate for any community and will not appear in the final
language.)
To solve this problem, libraries will enforce the use of one particular identifier for each separate entity in the system. Obviously the library designer knows best about how to use the entities modeled by the library, having carefully considered all of the potential use cases (and taking into account the language’s design principles), so there will be no clearer names than those provided. Some coders used to other, less maintainable systems, may object on terms of “creativity” and “expression”, but excess of consistency in service of maintainability is certainly no vice, where any good coder can tell stories of irredeemably creative symbol names providing no value to a system.
The compiler might even go as far as to include a part of speech checker to ensure that method and function names are verb clauses, variable and object and entity names are nouns, and aggregate data structures have the proper number, case, and pluralization. (Typos are a significant source of errors.)
External consistency is a problem with regard to specifications and implementations as well. Not only must maintainable programs be consistent within themselves, but they must be consistent with other programs. Even though many programs end up interoperating, where external consistency is obviously important, allowing even subtle linguistic and semantic drift in small pockets will only lead to difficulties in understanding.
Many programming languages, even those with formal specifications, fall afoul of the problem where the specification is ambiguous or an implementation does not implement the specification appropriately. To alleviate this, all implementations must implement the specification appropriately and no implementation will be complete unless it produces the same output for the same file as another implementation.
Put another way, no program will be complete and correct unless multiple implementations have compiled it to the same code. This suggests that the compiler should be a front-end to two or more separate compilers, ideally running on separate platforms. This need not extend the length of the compilation stage significantly if the tools take full advantage of threading and parallelization techniques, but by reducing ambiguity in the language many of the difficulties in parsing and optimization go away and it should represent a small investment for the sake of program correctness.

"""
To solve this problem at least, while respecting the principle of learnability and avoiding false cognates, a maintainable programming language should name all invocants after the name of their classes. That is, within a class named AlienInvader, all methods will automatically have access to the invocant through the symbol named alien_invader.
"""
Very bad idea - this introduces a too thight coupling between classes and methods.
Naming consistency within a library is good but this should not<\b> be enforced on the user of a library. The mark of a good library is that it can be used in ways the original writer never thought of.
A small, (lame), example: A complex number library might be written with i and j used consistantly for the eal and imiginary part of the number. Someone wanting to use the libraries complex number representation to represent points on a plane may rightly prefer to use x and y instead of i and j in his code.
Here you are already leaving your path of the "natural language". Even natural languages have shorthands for "objects". We call them pronouns. You say "I" and "he". And at almost all times the meaning of this alias is very clear. And it is very well defined in a programming language too, so that there never is a confusion about it's meaning. If you don't know what the pronouns are in a language, you don't know the language, and using it for something important will lead to desaster. If you don't know what a pronoun is "aliased" to in certain context, you don't know enough about the domain, and saying something situation can easily lead to (social/programming) desaster. Removing (predefined) aliases won't help the beginner (actually can confuse them), and cripples the the ones in the know and makes certain concepts (as generic programming) ridiculously cumbersome.
Let me simplify the problem I think you are trying to address:
People can name their variables anything they want.
In the real world, it is OK to name your cat Dog, but in source code, it is so bad for someone to be able to name a File object f, asdf, file, or config. A good name would be configFile. Certainly the others are easier to type, but they are very bad for readability. Oh and I have seen lots of code with variables like qaz, asdf, bob, etc. If it is a small method, then it might be OK, but not if it is used throughout a complex many line block of code.
So I agree that bad variable names are bad and hamper readability.
I will not have a dumb compiler telling me what I may or may not name my variables, or handles if you will. Good naming is a convention, and a courtesy to be extended, but making it a requirement is like communising the programming.
"Number 943, you may only name your dog fifi or fufu, but not dudu. It is against the best intsrest of Oceania"
Jessemon wrote:"Oh and I have seen lots of code with variables like qaz, asdf, bob, etc. If it is a small method, then it might be OK..."
If it's a small method, surely the language would provide pronouns (eg, Python's underscore: _ ), just as human languages do.
Good naming is certainly important. But why? It is important to know what kind of object it is (a file, an int, a string), which I equate to structure. However, using generic names for specific instances wouldn't solve the real problem. The more important question for maintainable code is, "how hard is it to figure out what is going on?" So even more important to the reader than the structure is the data it contains. If every file was named File1 and File2, the program would be totally unreadable and considerably more difficult to maintain. The kinds of names you want the programmer to use are ConfigFile and LogFile.
I also agree with the other comments that removing pronouns and replacing them with some convention is not going to clarify things to a beginner programmer, or create something closer to natural language.