Why Every Programming Language is Terrible
Tell me you've never hated your programming language. Tell me it doesn't have any features you wouldn't be caught dead actually using. Tell me there's a programming language so dear to you that you not only enjoy writing it in the moment but enjoy reading what you've written in the somewhat distant past. Tell me—and this is key—that you not only enjoy reading what you've written, but what others have written.
Now for the extreme version: is there more than one language you can say that about? More than two? Out of how many languages are you familiar with?
The more things change
How can this be? How do I know so many languages but really enjoy so few of them?
I'm at least proficient in C#, Elm, Java, JavaScript, OCaml, Python, Reason, ReScript, Scala, Scheme, TypeScript, and Visual BASIC. I've also dabbled in C, C++, F#, Haskell, Kotlin, and Ruby. I used to really like learning new languages, but it's honestly all a waste of time at some point.
Why is it a waste of time? Because of something all senior devs known to be true: programming languages are basically the same. If you know, like, three programming languages that are fairly different from each other, you have learned all there is to know about programming, and everything else is so far in the weeds it doesn't matter. C# is just Java with a silly naming convention. Java is just overly verbose Kotlin. Kotlin is just statically-typed Ruby. Ruby is just Python with braces. Yawn.
This is why Haskell has such a cult following among intermediate-level programmers and why it's known as something that can "make you a better programmer" even in other programming languages. It's true. If you feel your knowledge of programming start to plateau, learning Haskell or something Haskell-inspired is kind of a breath of fresh air that allows you to see things in a slightly different way. But again we have to ask: why is this experience of programming languages so rare?
And how can it be that most programming languages are so similar to one another when they're all adding features all the time? Have you seen the sheer size of the most recent C# specification?
The more they stay the same
What's actually new in your language(s) of choice in the past few years? Do you remember where you were when Python added support for static type checking? How did you take the news that ReasonML was rebranding as "ReScript" and adopting a TypeScript-like syntax? Were you regularly DuckDuckGo-ing "C# strict null checking when?" Did lambda expressions change how you wrote Java?
Now ask yourself: why did your languages make the changes they made? What reasons did they give? Overwhelmingly, the announcement blog post goes something like this:
We're adding a feature that so many of you have been asking for! Historically, we have been a language that does not use this concept, but we are introducing this concept because many programmers are coming from a language that does have this concept, and we want to make the transition as smooth as possible.
This new feature will allow us to target use cases which we haven't covered in the past. At long last, we can truly say we are a general purpose programming language, something that suits everyone's use case and makes everyone happy!
Also, we've added a JavaScript compilation target! 🎉
Oh.
Rust is just a C-like language in the category of memory safety. What's the problem?
It's reasonable to ask whether the problem I'm describing is really a problem. Maybe it's a neutral thing, or maybe it's actually a very good thing that languages are (1) responsive to user suggestions and (2) borrowing each other's best ideas. It is good in theory, but...
In practice, it means nothing works well. Every language wants to be Swiss Army knife, but have you ever tried to use a Swiss Army knife for something non-trivial? They're brittle and difficult to grasp and never have the size of flathead you need. They're absolutely the right tool if you can't carry a toolbox, but if virtually every tool maker announced their intention to only produce only general purpose tools, I'd never get anything done.
So every dynamically typed language adds optional safety checks and every statically typed language adds optional dynamic statements, so no one can rely on their checks actually checking 100% of the time. JavaScript adds object orientation as a syntactic sugar over closures, but the this
keyword (a pretty important thing in object orientation!) works counterintutively to anyone familiar with the languages they're copying. Everyone and their dog adds support for targeting node and browser runtimes, but their standard libraries throw exceptions left and right because they rely on sys calls not available in those ecosystems.
A second problem is that bloat matters in every piece of software, including compilers and interpreters. More code means more to maintain, potentially longer release cycles, and more potential for bugs and security vulnerabilities.
Again, general purpose programming languages are not bad, but it's worth considering why every programming language is racing to admit that they have no particular purpose. Whatever happened to using the right tool for the job? What's particularly sad is that a lot of these languages started out solving a particular problem and did it well.
If nothing else, think of it this way: don't you wish you loved anything as much as a Lisp programmer loves lisp or an Elm programmer loves Elm?
How do we blow it up? There's always a way to do that.
I think a number of incremental engineering best practices and cultural changes can help curb the issue.
Think about your stack before you start typing code. So much of the clamoring for features comes from organizations who chose bad tools to begin with and hit a wall after they're locked in. Is the tool you use really general purpose or does it have some underpublicized limitations? And would you be better off using some more specialized tools?
Don't take, "We just don't do that here" as an answer. Organizational inertia contributes to the above problem. Point out these problems to management when they say, "Oh, we can't use X because we're a Y shop."
Don't be a jerk about the specialized languages you hate. I'm going to say the quiet part out loud: just like there should be more languages that very, very closely match your preferences, there should also be a lot more languages that don't suit hardly any of your preferences. Don't discourage people from using them, and don't just say they're bad. Instead, try to make a good-faith effort into examining why someone would want to use a certain tool, and explain why someone with your use case would not want to use such a thing.
Push back when people float bad ideas. It's easy to hear about some new fad and think, "I'd never need that, but if it's what other people want, it probably can't hurt." Oftentimes, having a bloated language does hurt, but it hurts in subtle ways over time, so we have collective action problems wherein lots of people are clamoring for more new features and not enough people are clamoring for fewer. Ask yourself: would you want to see a bunch of merge requests into your code that use these new "features?" If not, advocate that your language remain specialized.
Applaud languages that decline to add new features. Ultimately, this is about changing the incentives for language developers (a lot of whom aren't exactly raking in the cash from their passion project). Even just a "Thank you" can go a long way. A few dollars' tip with it can go miles.
If enough people take a more thoughtful approach to long-term language maintenance, I'm confident we can build more appropriate tools and leaner, more innovative language designs.