> The best things are still a reasonably small core of concepts, with the development team working hard to “factorize” any new feature into those concepts, and the flexible syntax that is instrumental for making the core small
Incidentally, this is actually the definition of syntactic sugar, from a language design point of view. People often use it derogatorily to refer to bits of syntax that they simply don't like, but as a term of art it refers to syntactic elements that are introduced in an effort to provide new ergonomics—new ways of working in a language—without altering the language's fundamental primitives. As the article notes, in theory this method of language extension should actually keep the language simpler than if these elements were introduced as new semantic constructs.
There are a few places where this goes wrong in practice.
The first is that you end up accumulating a lot more syntax than you would if each syntactic extension to the language required a new semantic basis. It's easy to support syntactic sugar on a compiler or interpreter backend, so a language that is built on this small-core-with-sugar model doesn't have any natural forces urging it towards simplicity. To an experienced developer who watched all the layers of abstraction accumulate this doesn't pose much of a problem, but it's not always obvious to a newcomer what all this syntax desugars into, so for them it may as well be a new semantic element!
The other major place where I've seen sugar go wrong is that it rarely gets extra tooling to support it. A brand new semantic element will naturally need to be supported at every level of the language's stack, but the entire point of adding sugar is to avoid that extra work, so the language developers often just assume that the existing tooling will suffice. This leads to rough edges in things like stack traces and the debugger, because the code that actually gets executed only bears a loose resemblance to the code that the developer wrote.
It should be possible to avoid these pitfalls and have a tight language with a small basis and solid tooling, but it requires a lot more discipline.
Incidentally, this is actually the definition of syntactic sugar, from a language design point of view. People often use it derogatorily to refer to bits of syntax that they simply don't like, but as a term of art it refers to syntactic elements that are introduced in an effort to provide new ergonomics—new ways of working in a language—without altering the language's fundamental primitives. As the article notes, in theory this method of language extension should actually keep the language simpler than if these elements were introduced as new semantic constructs.
There are a few places where this goes wrong in practice.
The first is that you end up accumulating a lot more syntax than you would if each syntactic extension to the language required a new semantic basis. It's easy to support syntactic sugar on a compiler or interpreter backend, so a language that is built on this small-core-with-sugar model doesn't have any natural forces urging it towards simplicity. To an experienced developer who watched all the layers of abstraction accumulate this doesn't pose much of a problem, but it's not always obvious to a newcomer what all this syntax desugars into, so for them it may as well be a new semantic element!
The other major place where I've seen sugar go wrong is that it rarely gets extra tooling to support it. A brand new semantic element will naturally need to be supported at every level of the language's stack, but the entire point of adding sugar is to avoid that extra work, so the language developers often just assume that the existing tooling will suffice. This leads to rough edges in things like stack traces and the debugger, because the code that actually gets executed only bears a loose resemblance to the code that the developer wrote.
It should be possible to avoid these pitfalls and have a tight language with a small basis and solid tooling, but it requires a lot more discipline.