While working on my most recent language design I felt the need to delineate what I belive to be the fundamental precepts which lead to a good language design. That is the purpose of this document which is written independent of any specific language or paradigm,.
These are the two core precepts which lead to good language design. Simplicity brings the benefit of ease in use because no extraneous features are provided. Simpler languages are easier to learn and use. At the same time a language must be complete to be usable. The lack of completeness usually leads to toy and amateur languages. If a feature is necessary then it must be included in some form or else programmers will constantly find themselves wishing the language had some feature and spending time and energy working around the lack of it. These two concepts come together in a phrase which I find very useful in describing such systems
minimally complete which is used to indicate that something has everything that is necessary and no more. Determining what comprises a minimally complete feature set can often be challenging. There are however some precepts which can help, they are:
All to often the same language construct is used to mean many different things, this can be confusing. Consistency also concerns general ways of working, for example older languages used to limit the expressions that could occur in array subscripts even though logically what matters is not the form of the expression but that it yields a scalar value. Consistent languages are often simpler because they have fewer constructs and arbitrary rules. Consistency is also extremely important in its own right (I almost didn't put it under simplicity and completeness).
One of the most powerful transformations possible is to take two seemingly disparate ideas or features in a language and express them as a single idea or feature. This has the two benefits of simplifying the language and preserving it's completeness. Unification often increases consistency and power as well by allowing use of the feature in new situations.
Two features are orthogonal when they are independent and can be mixed freely. When two features can be used to achieve the same effect in some circumstances this is often an indication that they are not orthogonal. Languages composed of orthogonal features usually reduce complexity by removing redundancy among features but allow great flexibility because they may be freely combined with predictable results.
Entities may be
first class entities meaning that anything that can be done to other entities (or primitives) in the language may also be done to them. When as many things as possible are first class their is a reduced need for complex special restrictions.
When an experienced programmer fully understands the system he is attempting to model it should be easy to express that system in the language. Completeness can go a long way toward expressivity. However, there are two principles which can help a language be more expressive, they are:
The ability to abstract entities and processes allows the programmer to express natural relations while minimizing code.
No language can be written which contains
terms relevant to every problem domain, indeed such a language would
be far from simple. Yet the best way to discuss problems is with the
terminology developed for the field from which it comes. Allowing a
language to expand by allowing programmers to define new
(verbs and nouns) which act as first-class entities in the language is the solution.
Today programmers are increasingly required to rework code written either long ago or by someone else. Making the code readable makes that job easier as well as helping others trying to learn the language by reading it.
No language can ever be truly
self documenting for that reason it is important to include features such as comments to allow documenting. Tools like JavaDoc can also serve to improve the documenting of programs.
Nothing can ever be truly natural, instead it must appeal to something the user already knows from human or programming language. For example the fact that space serves as a delimiter and is not necessary around other forms of delimiter is quite natural to people with a background in germanic and romance languages.
When reading code it is very helpful to be able to see sections which are related at the same time and to see as much of the program structure as possible at once. However, recent innovations have increased the apparent compactness without shorting the actual code. Examples are, advanced ability to jump around the code and multiple views on the code. Creating diagrams such as UML can also increase the apparent compactness of code. Features such as the ability to provide literals with explanatory names can also help by not requiring one to look elsewhere to see what something is.