We use language as a means to communicate. It gives shape to how we express ourselves. But, it also feeds back and informs what we express, and how we express it. If a language lacks words to express a concept, we probably won't consider that concept when we think in that language.
Programming languages are no different. They are the tools we use to communicate with the computer, and also with each other. And like human languages, some have words to express certain concepts, and others don't. Also like human language, the programming language used helps define the concepts that are used in the program itself.
Another parallel is that all languages have subtleties and nuances behind their expression. These lend themselves nicely to poetry, or comedy. But with programming languages, these multiple interpretations can have disastrous consequences. Computers need very precise instructions, and when there is ambiguity, they tend to prefer failure over resilience.
Clearly we need to use a higher level language than that of machine instructions. Although these lower level languages are very precise, they lack methods of expression that are natural for humans, and are tedious for us to use directly. But, using human language is too complex and vague for computers to make sense of. Our challenge then is to strike a balance where we can more naturally express our intent, while simultaneously offering a precise definition for the computer to execute.
Taking a quick survey of programming languages shows that we've created our own tower of babel. In retrospect, it's not surprising that we ended up here though. Different problem domains call for different types of solutions. For some, performance is crucial, and therefore it's imperative to have access to the lower level mechanics of the machine. For others, the user experience is vital and performance is a non-issue. Programming languages run the gamut from low level memory manipulation to higher level meta languages. And as our hardware has evolved, our languages have tried to adapt by making the appropriate trade-offs as well.
Programming languages tend to be general by definition, and offer general purpose constructs to solve a wide variety of problems. Coming up with effective general solutions is critical, and it is the norm to require these. But, classes of problems do exist that demand certain particulars, and a language's ability to frame some of these solutions directly affects the shape that the solution will take. These can be domain specific languages, but they can also be picking a different programming style. For example, some problems are elegantly solved using logic programming. Others benefit from using a rules engine. Sometimes modeling using states and then following a transition graph is a good fit. Some of these can be used as a library to a language, but I would argue that languages offer different levels of support for these different styles, and it's more natural to reach for these kinds of solutions, since those concepts are a better fit for certain classes of languages.
When evaluating languages, brevity is often measured to determine expressiveness. Being able to model a solution using fewer parts generally means that it is easier to reason about, which ultimately means the system is easier to maintain. We have to be careful here though, because the important piece is that the system is easier to reason about, not that it's shorter. Just because something is terse, doesn't mean it's easy to understand. It could be conceptually dense with implicit meaning, which is the sort of thing that can lead to errors. If anything, we should strive to use constructs that prevent errors and encourage good practices. That being said, solutions modeled with fewer constructs tend to be more elegant. They often achieve this by using concepts that are better fits for the problem.
One mistake that I often make though is as I learn a new programming language, I try to think of it as the one true language that everything should be written in. Armed with my new hammer, I try to find problems that I can apply this language to, and end up trying to use it in many situations where it wasn't the best choice. I think that programmers are guilty of this kind of thinking in general though. Part of it is just that learning a new language is a significant investment, and we attempt to justify our investment by using it more. It's important to recognize when we have more faith in our language of choice than we should.
Particularly exciting for me is that we are now in a time where languages, tools, and libraries can evolve extremely rapidly, and can be shared seamlessly. Our tools to facilitate experiments with languages and approaches are only improving, and many different paradigms are surfacing as a result. I look forward to how our existing languages will continue to evolve, and to the languages of the future.