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
classintroduces 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).