It is obvious that it will not be possible to learn a new language every year and actually gain practical experience with it. But that is not important. Even those who only learn a new language every three years, or who occasionally deal with a previously unknown language, learn new things and extend their knowledge. That's all that matters.
It matters because not all programming languages are the same. Of course, this claim is generally true with respect to Turing completeness, but no one will seriously deny that some languages are more expressive than others, so that code can be expressed and formulated better, shorter or more accurately. If this weren't the case, everyone would still be working with the language that they originally used to learn programming.
Of course, there can be no objective selection of the best language. Perhaps it would therefore be best not to look for one language at all, but to reflect on the concepts underlying all languages. The more one knows these concepts and the better a language succeeds in putting them into context, the better the language is able to cope with any challenges. In other words: The concepts are far more important than the concrete implementation.
If I were to choose some programming languages, one of my favorites would definitely be Assembler. Why?
Modern high-level languages may conceal the basic concepts through abstraction, but they cannot go beyond their limits. No other language shows how algorithms work as well as Assembler. Anyone who knows how a CPU works in detail looks at the code that we write every day in high-level languages from a different perspective.
Joel Spolsky wrote his blog post Back to Basics on this topic almost 17 years ago, and it has lost none of its relevance and topicality to this day. His conclusion is as follows:
„If you want to teach somebody something well, you have to start at the very lowest level.”
In my opinion, there is nothing to add to that.
The American developer Eric S. Raymond once said that Lisp is a language worth learning even if you are never going to use it. What you learn from Lisp helps you in becoming a better developer in general. Therefore, it is obvious to take a closer look at the language.
If you are looking for an entertaining but well-founded introduction to the language, I can highly recommend the book Common Lisp: A Gentle Introduction to Symbolic Computation by David S. Touretzky.
The third language I would choose is Haskell. Unlike Lisp, Haskell uses a static type system, but does this in a completely different way than C# or Java, for example.
A common discussion among developers is about preferring a static or a dynamic type system. Often, however, the parties involved are not even aware that a static type system can have a different form than in the languages mentioned above. In this respect, Haskell can be regarded as a reference for comparing other statically typed languages.
In addition, Haskell is a purely functional language that does not have any imperative language constructs and forces functional aspects such as the immutability of data types. Haskell code therefore reads - just like Lisp code - very unfamiliar at first.
As with Lisp, however, it is also true that by being fundamentally different from the common C-like languages, Haskell forces us to think about new ideas. You benefit from this even when you return to your everyday language that you are used to.
Perhaps the least unusual language on this list is Go. Somewhere I picked up the phrase that Go is the modern C and that this is what C would look like if it had been developed in the 21st century. From my personal experience I can say that this is correct in many aspects.
Go is not only closer to C than the languages mentioned above, it also can be used in a similar way. Nevertheless, it offers much more modern constructs, for example for concurrency. In addition, the language has been simplified considerably compared to C, making it way easier to reason about code. In my opinion, Go is a good base language for system-related programing and for developing tools that have to be compiled as statically linked binaries for a variety of platforms.
Since Go is the least unusual language in this list, it is also the one that you can do without most easily. If you only want to learn three of the four suggested languages, skip Go. The gain in knowledge of the three languages mentioned above is likely to be far higher.
It is remarkable that I didn't choose a single object-oriented language in the style of C# or Java for this list. The reason for this is that these languages are very common anyway, so most developers are likely to be familiar with their concepts. Therefore, it makes little sense to recommend such a language for a look beyond the horizon.
In addition, functional programming is gaining a lot of momentum in the context of parallel code execution. I have taken this aspect into account for the list, although not all of the languages mentioned above move in this direction equally.
An important aspect, however, should be mentioned before we finish. I wouldn't advise a beginner to start with Assembler, Lisp or Haskell. Not because these languages are too difficult or too complex. On the contrary, if more developers took their first steps with one of these languages, it would be highly beneficial to their basic knowledge.
The reason for this is that it is very difficult to find a job if you only know one of these three languages. I try to bridge this gap with Go, which in my opinion is a very suitable language to get started with programming.
The other languages - Assembler, Lisp and Haskell - are rather a supplement to your everyday language and serve as a look beyond the horizon.