I don’t disagree on any of your technical points. But I also think for practical purposes you’re missing the forest here. I agree with the sentiment of the article - I think the big trend in general purpose PLs is a blend of multiple classical paradigms. Perhaps we’re moving the goalpost and paradigms need to be rearranged - but that is intrinsically interesting - it’s literally the continents of knowledge drifting slowly into new configurations.
Single-paradigm languages like prolog, CSS or SQL keep their restrictions not because the lack of use-cases, but because the benefit of keeping the complex execution engines away from the end-user exceeds the minor wins in expressiveness.
I don’t think it’s a coincidence that the declarative languages are in this category. They are higher level, and opening up low-level customizations is really tricky: for instance, if you put imperative code inside your CSS, it needs complex “re-evaluation rules”, that results in a dilemma: either give full control to the programmer, which imposes specific execution engine designs and complex API surfaces – or re-evaluate too often, which risks killing memoization and perf (cache invalidation). It could be even worse if the code has side-effects or dep cycles.
On the contrary, imperative low level languages like Rust can easily come along and say things like: “this is not only a function, but a side-effect free function”. “This is not just a reference, but an immutable reference”. Then you can cleverly leverage those traits in your “execution engine” ie the compiler, to deliver low-level perf. There are even people who describe rust as a high-level language for these reasons, which is a bit provocative to me but in all honesty not completely outrageous.
An alternative take on the last 10-15 years:
- General purpose PLs typically have an imperative base, while integrating multiple classical paradigms:
- Only a few aspects of OOP are added to modern PLs, where inheritance has largely been superseded by simpler composition
- Features from FP have surged in popularity, being integrated and even retro-fitted into general purpose PLs, providing both perf- and DX improvements
- Structured meta-programming and/or codegen has been a strong focus for compiled languages, acknowledging that it’s preferable to limit the complexity of the core language at the expense of separate pre-compile phases