There are a big number of programming languages. Some of them grow up and become very popular. People use such languages more and more often. The founder of such language (or founding organisation/community) may try to implement changes to make the language better. But sometimes it’s hard to make some changes because of backward compatibility and such ugly things have already existed in the language for years, and are used by many users.
Are there any architectural principles or steps, during the language design phase, which can help to make it more stable so the language designers won’t be as afraid to break backward compatibility?
5
Language stability is not a technical decision. It is a contract between the language author and the users.
The author advertise a given version as more or less stable. The less stable a language is, the more changes the author can make. Each user interested by the language can decide if he wants to invest time in it to learn new features or develop applications that might be broken by next month’s update.
Using an unstable language can be interesting because you are interested by a new concept, or you want to help by giving your feedback. If you are a business, you might prefer to wait for a technology to be more stable before investing your time in it.You care more about stuff like time to market, and user experience.
So this is a communication & trust issue. Look at the rust language development. They are crystal clear about what they are changing and what they are keeping. When they want to delay a decision about a given feature, they use what they call a feature gate. On the other side, the angular team faced a lot of anger over their 2.0 announcement because the changes were larger than expected.
Even libraries author have to communicate about the stability of their apis. Pretty much any technology used by other people have to strike a balance between stability and perfection. A car maker cannot change the position of the pedals, and a laptop designer won’t invent a new keyboard layout for the same reason: you are not helping your users if you cannot make a decision about the way they will use your product.
- Be aware that languages change throughout their life, regardless of how well it might be designed up front. Instead of trying to immediately ship the most awesome language on earth, first try to be useful and extensible. A mediocre langauge which I can actually use is worth more than any wonderful programming language that only exists in theory.
-
Consider facilities to make the syntax extensible, e.g. macros. Macros are not automatically a good thing and can be too powerful. Some languages have a very flexible syntax from the start which reduces the need for macros. A couple of scenarios to consider:
- Can I introduce a new operator such as
|>
without leaving the language? Can I choose precedence and associativity for this operator? - How much ceremony do I have to go through for an inline function/lambda/closure?
- Can I use existing language syntax to implement a foreach loop syntax? E.g. Ruby and Scala can do this through their flexible method call syntax with lambdas.
- Can I introduce a new operator such as
-
Consider facilities to keep semantics extensible. Common needs are:
- Operator overloading, where user-defined types can assign their own meaning to existing operators. This makes a language much more enjoyable in maths-heavy applications.
- Literal overloading. Can I make string literals be of my own string type? Can I make all numeric literals in the current scope be bignums?
- Metaobject protocols. If the language doesn’t have traits, can I implement them inside the current object system? Can I implement a different method resolution order? Can I swap out the way objects are stored or how methods are dispatched?
- Have regression tests. Lots of test. Not only written by the language designers, but also by users. When adding a feature breaks these tests, carefully weigh the benefits of that feature against the benefit of backwards compatibility.
- Version your language. Not just in your documentation, but also in the source code itself. Once you do that, the only part of your language that cannot change is this version pragma syntax. Examples: Racket allows you to specify a dialect. Perl allows you to
use v5.20
, which enables all backwards-incompatible features of Perl v5.20. You can also load single features explicitly likeuse feature 'state'
. Similar: Python’sfrom __future__ import division
. - Consider designing your language in a way that results in few reserved words. Just because
class
introduces a class does not imply that I wouldn’t be able to have a local variable namedclass
. In practice, this results in keywords that introduce variable or method declarations, counter to the C-like tradition of using type names to introduce declarations. Another alternative is to use sigils for your$variables
, as in Perl and PHP.
Parts of this answer are influenced by Guy Steele’s speech “Growing a Language” (1998) (pdf) (youtube).
2
I think a pretty important step is to promote a package manager whicih can also manage the version of the language itself.
For instance, I use SBT for Scala or Leiningen for Clojure. Both of them let me declare which version of the language I want to use, per project. So it is quite easy to start green projects in the latest version of the language, while upgrading the existing projects at a more comfortable pace, if ever.
Of course, depending on the language, this may still leave you with the need to wait for the relevant libraries to be ported to the version you need (this happens, for instance, in Scala), but makes things easier nevertheless.
2