Imho, some of the things (not exhaustive):
- It should be easy to read (and write): consistent, balance conciseness and explicitness, not too many ways to do one thing...
- Help users avoid mistakes or catch them as early as possible. Types are very important here, warning then you do something nonsensical like add a primary key to someone's age. Similarly languages can offer features to prevent dangling pointers, null pointer exceptions, segmentation faults, memory leaks, race conditions, unsafe casts, forgetting enum variants...
- The right level of abstraction. For example, for one language, garbage collection might be a good design choice, while it wouldn't be for another. Goto is suitable at the extremely low level, but loops and conditions are a good abstraction in other cases. Sometimes shared memory is better, sometimes message passing, sometimes automatic loop parallelization. There shouldn't be too much boilerplate.
- Good encapsulation capabilities (especially important for large projects). To reason about code, you should be able to feel confident about where your changes may have an impact, which should ideally be as small a scope as possible).
- There are many exceptional cases and implicit conversions. It has quite some historic baggage, and the obvious way to do something if often wrong (e.g .
==, using objects as hashmap keys, initializing a variable without
- Many things that are probably not sensible to do just keep a JS program running, like type conversions or accessing undefined properties or passing too many/few arguments.
new), you have to think about many language details instead of just the objects.
- Since JS was initially designed for small scripts, there wasn't a module system, scoping was sometimes terrible (
varoptional), and certainly no package manager. Some of that has improved (and one might argue that a package manager is not part of the language).
That said, the JS community did a good job working around the fact that many things weren't initially built-in. But I don't feel that stops the language from being bad. A good language shouldn't just allow experts to write good code, it should encourage beginners to do so.
If the industry had a consensus about what constitutes a well designed language, there would be far fewer languages than what we have now.
Every new language coming into existence is being designed by people who are not satisfied with any of the existing options, and decided to make their own.
The designers of Go-lang think it's well designed, but many others disagree.
Some disagree because they think lacking generics is bad.
Others disagree because they think a GC is slow and communicating via channels is slow.
The designers or Rust think it's well designed because safety is of the utmost importance.
Others disagree because they think Rust has high friction.
Some language designers want to prioritize "enabling good developers to get work done", while others prioritize "preventing average/mediocre programmers from making errors".
Some people think a well designed language has compile time type checking, others think dynamic typing is better.
Now, when it comes to my personal opinions, here they are:
The official compiler should be really fast. It should be able to compile a large code base in under 1 second.
It should be able to cross-compile to other platforms with minimum effort.
Compile time type checking is absolutely essential.
It should be able to effortlessly compile the project to a single statically-linked native binary executable.
It should enable a mode of programming where the programmer has control over memory (writing custom allocators, running without a GC, etc), while still providing ways to help the programmer to just write code without having to worry about these things when needed (e.g. optional GC or ARC).
Thing is I wouldn't consider C# or Go any more "well designed" but that's because I learned machine language first, and my first high level languages were all Wirth family. (Pascal, Modula, Ada)
The term "well designed" is sufficiently vague that it means different things to different people. I prefer languages that are verbose and clear, that use as few tokens and symbols as possible. I like strict typecasting, well thought out object models, and a host of other things that C#, C++, Go, PHP, and a plethora of other programming languages JUST DON'T HAVE...
It's more a case of the right tool for the right job, and MOST of the time someone complains about a specific programming language being "poorly designed" it's more about them trying to use it for something it was not intended for in the first place.
... though there are still languages I just don't get, and if I were to make a list of what languages I consider "poorly designed" it would include Ruby, Python, and Rust. I find their syntax, methodology, and mindset pointlessly obtuse, and question just about every aspect of how they work. FOR ME they are harder to use and counterintuitive in nature compared to more traditional languages -- which is why (much like frameworks, or when people use the word "quality" in relation to crApple products) when people talk about them being "easier" I genuinely have zero huffing clue what the blazes they're talking about.
Hence the whole funneling beginners on the Pi into Python confusing the ever living piss out of me. It's the LAST language I'd point beginners at; particularly since most every concept of how it works does NOT translate well to other languages.
... and knowing MORE than one language is important so that -- as I mentioned above -- you can use the right tool for the right job!
As much as I hate C and consider it poorly designed, it's where most people SHOULD be starting if you can't "man up" and learn assembly.