Page 1 of 3
How should we program?
It's not a difficult question to ask - but to answer it is another matter.
It would be nice to say that we are where we are, using the languages that we use and holding the views on what is good and bad by design, by careful thought and a lot of theory, but this would be wrong.
To make it clear from the very start there is no single theory of how we should program that is provably better than the rest.
What are clear are the objectives of a good language. A good language should be easy to use, should make it almost impossible to introduce bugs and should create programs that are easy to understand and easy to maintain.
When expressed in this Utopian manner it is immediately clear why these objectives are not achievable, only approachable.
Consider for a moment the most intelligent and long tested programming system of all - tell another human to do the something. Even with the most intelligent computer on the planet (allegedly) and a flexible natural language honed over thousands if not tens of thousands of years things still go wrong. Yes natural language is great at conveying subtle meaning, poetry even but as a way of specifying a task it tends to leave much unsaid.
So what are the possible approaches to designing a programming language. Even here it is difficult to be precise as a given language can take a little from each of the key approaches and so be a blend and not fit into a single category. However we can identify two broad strategies:
Procedural v Declarative
The split between the procedural and the declarative is really the big dividing line at the moment and it represents two distinct ways that programming could go. Let's look at each in turn.
Procedural, or imperative, programming was the way we first approached programming a machine because it corresponds most closely to the way the machine works. A computer takes one instruction at a time and carries it out or "executes" it.
A program is just a list of executable instructions and a program overall gets the machine to perform a procedure - adding two numbers together say. The reason it is often called "imperative" programming is that an imperative is a command like "run!" or "stop!" hence imperative programming is about putting together lists of commands.
Essentially the procedural approach to programming is all about telling the machine what you want it to do in minute detail
add 1 to 3 and display the result
is a procedural program in English.
Over the years the procedural approach has become increasingly sophisticated and abstract. Starting from the low level machine instructions we have worked our way up to bigger and bigger instructions that achieve more and abstract away from the machine's hardware. Early procedural languages were about the machines they ran on - later languages were and are more about the problems we are trying to solve.
The main process in the development of procedural languages has been the construction of a hierarchy of procedures or subroutines which eventually become formalised into language commands. So back in the early days you would have constructed a sort program using registers and low level branches, then you would have organised this into a sort subroutine which could be called and then on to a sort command embedded int the language. In most cases the step to include the new command in the language is never taken because its more efficient and flexible to simply create a standard framework consisting of a function library.
The big breakthrough in procedural languages was the invention of object oriented programming. This allowed programmers to group data and procedures into a single package - the object. At one level this is just a practical convenience, keeping code with the data it is going to operate on. At another it is a powerful step up in abstraction. Code now attempt to mimic the behaviour of objects in the real world. Chunks of software have states and behaviours just like real objects.
Today the situation is that you can have procedural programming without objects but its generally considered to be a regrettable return to old and inefficient ways. Even so many object oriented procedural languages are used as if they were first generation languages and objects are simply ignored or used in a simple non-object way.
You can provide the tools but you can't make a programmer use them.
Procedural programming seems to natural and obvious that it is difficult to see what alternative there is. Declarative programing is based on the idea that rather than specifying exactly how a problem should be solved you simply state the problem and perhaps the objective and let the system figure out how to achieve the result i.e what procedure is necessary to give the objective from the problem. Put like this declarative programming sounds as natural and as obvious as procedural.
For example, if you give a task to a human, i.e. you want to program them, you can either break the task down into the steps needed to solve the problem or you can simply state the problem and let the person sort out how to solve it.
This example also indicates that there is a continuum between procedural and declarative that depends on how much of the solution you provide and how much you expect the computing agent to provide. It's all a question of what you assume the agent knows. Another way of looking at this is to think of the shift from procedural to declarative as a moving of the "intelligence" from the program to the agent.
For example, in the early days we would have written a sort program in minute detail specifying the exact sorting algorithm to be used. Today we probably just use a sort procedure supplied as part of the language framework and leave the method used up to the procedure.
It is important to note that this interpretation isn't the one most often used today because declarative approaches to languages tend to attempt to banish all notion of procedure from what is going on and not just cover it up or encapsulate it in a black box procedure. To see what this is all about we need to look at some specific examples of declarative systems.