Programming languages are a favorite subject of debate among developers. Each developer has precise and sharp ideas about:
- which programming language is the best to solve any kind of problems;
- which programming style is the correct one to develop effective programs;
- which is the best way to arrange brackets, semi-colons and all that in a piece of code.
One of the striking features of Dr. Subramaniam’s talk, held at Codemotion Rome 2019, was to show how much such matters are negligible in the first place.
His approach, engaging and entertaining, is that programming is a practice activity, a living one, and it is an incarnation of problem solving. Moreover, actual coding is a small part of the game when developing a project, but tools used to program have a strong influence on the final result and the main tool of a programmer is the programming language he/she codes in.
To program is to communicate
A programming language is essentially a tool to communicate between programmer and machine and a form of expression just as other kinds of languages. Therefore, to program is to communicate, and as such programming is neither a science nor an art but rather a combination of the two, just like any other kind of communication. Think for example to human verbal intercourse.
However, programming languages are different from human languages in many respects and, in particular, because they come in very different flavors. Of course, they share some basic common features, for example they have to be “Turing complete”, thus able to express any kind of computable function but, as Dr. Subramaniam nicely observes, to say that a language is Turing complete is much like saying that any human can sing!
More features have to come into play, such as ease of use, elegance of expression, performance in execution, efficiency, usability, maintainability, etc. Indeed, programs are not only written to be executed by machines, but also to be read by humans (for example when maintaining them), as the CS adage says.
Comparing programming languages
It is difficult to compare all those features but, from the programmer’s point of view, an effective way to compare different programming languages is to look at their typing system, thus how they classify data and information, and how they let the programmer express, define and manipulate that data, therefore, how to write algorithms on them.
As far as typing is concerned, we may imagine, following Dr. Subramaniam’s talk, two dimensions which give rise to a Cartesian plane where statically-typed languages are above the x axis and strongly-typed languages are on the right of the y axis. Let us recall that a language is statically-typed if the type of a variable may be inferred at compile time, while it is dynamically-typed if the information about the type is only available on run time. A strongly-typed language is such that types are fixed and casting needs to be explicit (if available) while a weakly-typed language lets the programmer get rid of type constraints quite easily.
In the following picture we illustrate the positions of some widely-used languages:
A nice sentence we took away from the talk was “Javascript is like an infant: it’s cute to play with but you have no clue why when it begins to cry”: this is the best explanation of the pros and cons of the different combinations among static vs dynamic / strong vs weak typing systems.
Programming paradigms
However, the typing system (often not completely understood by the average programmer) is not the only feature a language may display to attract, or repulse, a developer; another great arena for different opinions about what a language should be is the paradigm, thus the devices the language provides to let the programmer pursue his/her tasks.
There is not a definitive agreement of the classification of paradigms, but following Dr. Subramaniam’s talk we may mention the following ones:
- Procedural paradigm
- Structured paradigm (more or less the same as procedural in modern languages)
- Object oriented paradigm
- Functional paradigm
More could be added but actually, a common thread emerges between all those paradigms, best summarised in the following David Wheeler quotation: “all problems in computer science can be solved by another level of indirection”. We can display in a simple table what a level of indirection is in the different programming language paradigms:
Paradigm | Indirection |
Procedural | Pointer |
Object oriented | Polymorphism |
Functional | Lambda |
Notice that Lambdas, the basic device of functional languages which consist essentially of allowing functions as first-class objects, are considered as indirections because of the lazy evaluation feature, available in all modern implementations of functional languages.
An often misleading distinction is the one among functional paradigm vs object oriented ones: on the one hand, this is not actually a distinction anymore, since Java, C# and all that provide Lambdas, maps and all tools needed to express the functional paradigm. Instead the real difference is between imperative and declarative approaches.
In short, languages complying the imperative approach urge the programmer to tell them what and how to do some task, while the declarative approach consists in telling them what to do without caring about how to do that, leaving this detail to others. For example, encapsulation is one of the ways to say “I don’t care how it’ll be done”.
The functional paradigm may be described as joining the declarative approach with the higher order functions facility, as Dr. Subramaniam puts it:
Imperative means hard to read but easy to write.
Functional means easy to read but hard to write.
That implies that either approaches may be better in different contexts: it just depends on the point of view. Imperative programming still remains efficient and maintainable when side effects and exceptions are needed (think instead that purely functional languages should completely avoid side effects), while functional programming is great at scaling, factoring and easily expressing the logic of the program.
So we are condemned to live with both of those paradigms, and indeed modern versions of object oriented languages provide plenty of functional constructs, for example consider Streams in Java 8: hybridisation seems to be the way to let languages enable and not limit our form of expression and we have to accept the duality imperative/functional as much as we accept the duality wave/particle in optics. Both points of views are needed to get a full understanding of the light phenomenon and both imperative and functional approaches are needed to unleash the expressive power of programming languages into efficient, maintainable and beautiful programs.