I think Go is the perfect example of this. On paper, it's a simple language. But:
- Not having sum types means it's awkward and unsafe to express concepts such as "X or Y". This is a big deal, because "Result or Error" is one of the most common ideas we have to deal with in programming. I think this tweet visually captures the awkwardness: https://twitter.com/GabriellaG439/status/1521860707444133888. Another place this shows up is pointers: in Go, since there is no way to represent "present or missing" at the type level, Go has to bake that possibility into the semantics of pointers. The billion dollar mistake.
- Every type in Go has a "zero" value. On the surface, this seems simple: when you declare a variable, you get a predictable value without having to explicitly initialize it. But this prevents you from implementing abstract data types which are guaranteed to be constructed by a smart constructor that establishes all the relevant invariants. Now you always have to worry about this zero value, since it might not satisfy the invariants of your data structure (consider the simplest data structure with an invariant: a pointer which is non-null!). Also, you can easily forget to set a field in a struct, which means the zero value will show up in unexpected places (resulting in subtle bugs that might go undetected even at runtime).
- Until recently, Go didn't have generics. Simpler, right? But that means if you want to build a reusable data type, you needed to either (a) make N copies of it, being careful to keep them in sync, with no help from the compiler, or (b) sacrifice type safety and represent data as interface{} (essentially a void* pointer), adding dangerous casts all over the place.
Languages like Haskell and Rust are more complicated than Go. But once you've paid the upfront cost of learning them, common programming patterns actually become simpler.
Of course, there's a flip side to this. Most languages have a lot of accidental complexity too, which isn't what my argument is about. So I almost hesitate to make this comment, fearing that it will be quoted out of context to justify adding badly designed features to programming languages.
But, people can like what they like, it's all good.
In such a small system, the benefits of any feature are much smaller. Let's say you have some feature that could simplify your program by 1%. Over 10,000 lines of code that means save 100 lines of "complexity". If implementing that feature takes more than 100 lines, your small 10K lines system will end up more complex, not less.
Go however is fairly widely used. Simplifying by 1% on average is huge, and likely worth it even if it requires 10,000 lines of code all by itself.
I think many languages have some implicit set or class of functions in mind, and some of the frustrations people encounter is in a mismatch between what the language was designed for and what they want to apply it to.
I still think minimalism comes into play as a language design goal for an application domain, in the sense some languages are unnecessarily obtuse. But I also think there is something else — I'm not sure what the term is, but "scalability" or "abstractability" comes close — related to a language's capacity for minimalism at multiple levels of abstraction. That is, can you simplify code as need be if youre not interested in the details, but delve into the details if need be?
That gets to the point.
But there is a lower bound of minimalism, below which the complexity increases again; I remember the time where a few academic ergonomists spread the idea that you should have only one knob in the user interface instead of many elements; the result was that you needed many more steps for each function and the operation was anything but intuitive.
It's the same with a programming language: if it's designed too spartan, I have to write too much code that is always similar, which causes effort and obscures the real intention of the code. And if the syntax is too minimalistic, complexity is just moved to semantics or the standard library. So the art is to find the right balance, not just to omit.