I’ve been thinking a lot about why I gravitate towards certain languages and revulse from others. Or as I put it on Mastodon, “I reject languages that are almost exactly to my taste because some relatively small thing I don’t like becomes an unbearable affront to my sense of good taste, and so I return to C++, a language I can find almost no redeeming value in.”
That’s overstating it a bit, but there is some truth there. I know C++ pretty well, and obviously familiarity is part of the reason we keep reaching for a particular tool. But that doesn’t really explain it. Why do I like C++ and have so many complaints about languages that objectively seem better fits for what I believe I like in a language? What do I actually like in a language?
Part of the difficulty I have in expressing this is my tastes seem really widely varying. I like C++ and Clojure, and it’s pretty hard to put one neat bow around that pairing.
Simplicity of Conceptual Model
People who like cars or airplanes talk about feeling connected to the controls. It’s that “I want to feel like the steering wheel is mechanically linked to the tires” thing. I like the equivalent in programming. By that, I don’t so much mean “close to the hardware”. I mean something much more like “the code I write is close to the intent I have in my head”. If I want to get a list of all users who have logged in today, I want to write code that looks like I’m querying a database for users who have logged in today and not write code that looks like I’m asking some abstract black box to translate my intent for me.
In terms of newer languages, Go probably comes closest to hitting my ideal here. Functions that can transparently return multiple values and explicit error handling feels better to me than exception handling. A main that calls functions feels better than deep object hierarchies. Give me a safe string interpolation facility that lets me safely write simple queries instead of a big ORM that abstracts away my database.
Simplicity of Management
I’m using this umbrella term to refer to a range of things like compilation models, complexity of dealing with the compiled artifacts, library management, etc. And “Simplicity” was a precisely chosen word here. Easy is great, and I’ll take it if it’s on offer, but Simple is what I really want. I’d rather deal with Make than Maven. Maven is better in pretty much any objective comparison, but Make is Simple. There’s no magic in a Makefile – it’s innards are laid completely bare.
I don’t really like dependency management in a language. That feels like a job for my package manager. There are admittedly lots of benefits to having things like venvs that wrap an entire environment in a reproducible bundle, and I use them when it’s appropriate. But I don’t like it.
Interactive Development
Lisp-family languages do this really well. It’s not just about having a REPL. Python has a REPL and it’s more or less useless for the kind of interactive development I like. Python feels like you can use a REPL to try something out, but trying something out is cumbersome, and if you’re happy with the result, the outcome is that you can update the “real” code so that the next time you run the program, it reflects your change. Languages like Common Lisp or Clojure encourage a development style in which you have a single long-running process that all your development tools are effectively living in, and that process is the same as the process that runs your application.
Thin Abstractions
Another think I like about Clojure is this idea of data-oriented programming. As the Clojure folks are fond of saying, “Just use maps”. This is an idea that works fantastically well with functional languages, but Clojure in particular seems to really lean into it. Rather than model data as classes and build structures that aggregate instances of those classes and logic to operate on those structures, Clojure says just represent those structures using native data structures in the language and native tools that operate on them.
I tend to dislike things like ORM layers that are heavy, require substantial tooling support, etc., that result in feeling like I’m too far removed from the thing I’m trying to do.