Page 2 of 3
It is also worth knowing that the procedural/declarative split is also used in a lesser sense when applied to situation like UI construction. Markup and object creation initialization languages like XAML or MXML are called declarative because you write a specification of a UI using them and don't care about how the objects representing the UI are created. The procedural approach is to call object constructors in a given order and initialize properties explicitly. If you think about this for a moment you will realise that this distinction really is trivial. The real reason for using object creation and initialisation languages is that they provide a language neutral way of creating a UI which makes the implementation of graphical designers easier. In other words, markup/object creation languages v procedural languages is not a deep issue.
To see what the issue really is you have to take a look at the two best known approaches to declarative languages - functional and logical languages.
Functional languages try to reduce, or is it enhance, programming languages to mathematical functions. That is, when you use a mathematical function its result never changes - the result of Sin(0.3) is always the same and a function is just a lookup table. Sometimes the lookup table is too big to be stored and in this case the result is returned after being calculated using a procedure - but notice that all mathematical functions can in theory be implemented as lookup tables.
This isn't so with programming functions or procedures. (Note: a function is just a procedure that returns a single value as its result. All procedures can be cast as functions by the simple trick of having them return a single but perhaps complicated structure constaining all there results rolled up into one object.)
A programming function can produce side effects and state changes. For example:
In this case each time the function is called it returns a new value - it is counting - it is procedural - and this can't be allowed in a functional language.
In a functional language the sort of counting described above isn't allowed and variables aren't variable in the sense that once defined they have that value for the duration of the program - they are "bound" to their value. At this point non-functional programmers generally give up because this is nothing like the way they think. How can I have loops and conditions if variables never change?
There are two answers to this question. The first is why are you counting? If the counting is part of an algorithm that gets you to a solution then don't do it. Simply get the solution. That is, if you implement Sin(0.3) with an iterative calculation then simply hide the iteration behind the function and return the result.
The second answer is that there are functional ways of doing the same job - mostly recursion and functional composition. For example, if f(x) is a function which returns x+1 then counting can be implemented as:
and so on... Notice that in the nested set of functions
only one variable z is bound to the result, no variable change their value and even so there is still an "increment" as you work your way into the brackets. In principle the function do do something with the count much as you would in a for loop say. This is the functional languages equivalent of the fixed repeat for loop. There are also equivalents of for loops that perform variable numbers of repeat using either functional composition or recursion.
Functional programming has big advantages in that it doesn't specify how functions should be computed only that they should return their result. This means it can be easier to see what is going on and easier to perform implementation based optimisations such as parallel execution. Of course under the covers there is a procedural implementation whirring away - isn't there always.
Logic programming is another approach to the declarative style and in many ways it is more true to the ideals than functional programming. Logic programming is typified by a single language and dialects of the same - Prolog. The language itself was developed in 1972 but it was based on earlier advances in logic. Logic programming is a bit like functional programming in that it generally doesn't allow functions or variables to change their value but it has one extra ingredient - proof.
In a logic programming language you can write a statement like:
and the language will return true if A and B are connected and false otherwise. This may sound trivial but it even works if A and B aren't directly connected but only connected by a chain of intermediates - perhaps thousands of intermediates. The language has built into its core a recursive search facility that will "prove" statements according what what is stored in its database. For example if you write
where x is an unbound variable then the recursive search will attempt to find a value for x that makes the statement true. More generally you can write:
and the search will find a combination of values for x, y and z that make the predicate (a function that returns true or false) true.
Prolog is a general purpose language and it has facilities for input/output, graphics and so on - but most programmers will admit that the methodology is a little strained when you are working with predicates simply for their side effects.
Some problems are very easy to solve using logic programming. What is encouraging is that many very difficult problems suddenly become easy the real question is how far can you go. Today it isn't clear if logic programming will lead to anything more or if it is best treated as a specialist approach - a sort of functional programming with logic thrown in.