melp.nl

< Return to main page

Declarative is the new black.

The paradigm shift in the purest sense of the expression. The old paradigm makes way for new ones. And declarative is the new one. Well..., not new new ...

The "old" paradigm: Procedural

Procedural programming is a form of imperative programming in which you define "procedures" or "subroutines"1 that encapsulate a certain set of operations, and accept parameters or "arguments" to that set of operations. A very simple example of this, is a procedure that will return the sum of two arguments. In C:

#!c
int sum(int a, int b) {
    return a + b;
}

We declare a procedure that accepts two integer arguments a and b and return an integer sum of the arguments. Simple, efficient, clear. If we want the sum to be present in a third variable, we simply execute the procedure:

#!c
#include <stdio.h>

int sum(int a, int b) {
    return a + b;
}

int main(int argc, char *argv[]) {
    int c;
    c = sum(10, 20);
    printf("c now contains %d\n", c); /* Will output: "c now contains 30" */
    return 0;
}

Procedural is imperative

The procedural style of programming is imperative. Imperative means that you program the execution flow. You decide what pieces of code gets executed by using control structures, loops, procedures, etcetera, and the state of the variables (the "context" or "environment") ultimately define the execution flow. This is very intuitive, since you can read most imperative languages as "if something happens, you do this and that". More or less like an instruction video for driving. If someone comes from the right, yield.

The "newer" paradigm: Object Oriented

Another, still imperative, paradigm is Object Oriented programming. Though it is not strictly different from procedural programming, it introduces a new concept: binding data and methods to a single object, that represent one single previously initialized state of the object. This is called instantiation and means that multiple objects of one type can exist in memory simultaneously. Each of these objects have their own methods that act only on the state of that particular object.

There are two concepts that are new in OO style programming, first of which is encapsulation, where the outside world does not need to know what is going on within some object's internals. Second is polymorphism, which means that an object can explicitly express that it is capable of some sort of functionality and may implement it in a very different way than another object which exposes the same behaviour. Huh? Bear with me. Here's an example.

#!java
class AreaCalculator {
    public float total = 0;

    public void add(HasArea obj) {
        total += obj.getArea();
    }

    public void subtract(HasArea obj) {
        total -= obj.getArea();
    }
}


interface HasArea {
    public double getArea();
}

class Rectangle implements HasArea {
    private double _side1;
    private double _side2;

    public Rectangle(double side1, double side2) {
        _side1 = side1;
        _side2 = side2;
    }


    public double getArea() {
        return _side1 * _side2;
    }
}

class Circle implements HasArea {
    private double _radius;

    public Circle(double radius) {
        _radius = radius;
    }

    public double getArea() {
        return Math.PI * Math.PI * _radius;
    }
}

class Example {
    public static void main(String[] args) {
        AreaCalculator calc = new AreaCalculator();
        // our yard is 10 by 4.2 meters
        calc.add(new Rectangle(10.0, 4.2));

        // But it has a pool, 3 meter in diameter
        calc.subtract(new Circle(1.5));

        System.out.println("The square metrage we need to buy grass seed for is " + calc.total);
    } 
}

The result:

The square metrage we need to buy grass seed for is 27.195593

The example shows that two different objects have some common characteristic. In this case, they both know how they should produce their own area. The implementation is bound to the meaning of the class itself, since the area of a circle is calculated differently from the area of a rectangle. So, they both provide their own implementation to do so. Since the two different implementations are tightly related, it does make sense that we defined a common interface for it, so the actual consumer of the objects (the calculator) does not need to know they are totally different kinds of objects. All it needs to know is that the object is capable of producing it's own area. That's it. Well, if you know OO you know this already, but you might not have known that this phenomenon is called polymorphism. The fact that we hide the properties _side1, _side2 and _radius from the outside world is encapsulation. Especially polymorphism is something that is hard to achieve with a purely procedural language.

It strikes to see that OO versus procedural, however still imperative, is much more descriptive. It describes natural things in a natural way. A pear is a fruit, an apple is a fruit, they both have some specific characteristics, but they share a lot of common properties and probably operations, such as feed() or ferment(). The programming style still is imperative as it follows the execution flow, but much of the organisation of the code revolves around typing and classifying, so it is more descriptive even though not yet strictly declarative.

It would also appear that OO moves focus from processing and decision-making to behaviour. Describing behaviour is typically part of the object model, because behaviour defines how objects interact and how they depend on each other. This is curious: the data no longer plays the main role in the design of the program, the behaviour does. Let's stash that thought.

Declarative programming

The main difference between declarative and imperative programming, is that declarative means that you express what something is, rather than what something should do. A common example of declarative programming is build scripts. I'll use a Makefile for this example.

As we define a task within a Makefile, we can specify what tasks it is dependent on. As this simply declares what the task does, without actually doing any decision-making within the program this is typically a declarative approach. The tasks get declared as what set of executions the represent. The decisions that are needed to make for execution of the tasks is made by the platform:

#!Makefile

# building the app depends on mysum.c (the example above)
# if mysum alters, the app should alter too
mysum: mysum.c
    gcc mysum.c -o mysum

The approach is declarative. We do no checks, we just tell what needs to be done to generate the app. See the entire Makefile for the above examples on the github repo

Another relatively simple example is Haskell. Without explaining too much about functional programming, the main idea of Haskell is that you declare anything you need. Declaring things does not mean executing them. For example:

#!haskell
l = repeat 5

What happens here is that we declare a list that is made up of an infinite list of 5s. So, how come nothing happens? If the list is infinite, how can it exist? It exists because it is declared. It only gets meaning as soon as it is used in context, which means that the meaning is defined by evaluating the list in it's context. When (or even if) this happens. The problem is, imperative programming leads to the assumption that everything that is actually declared, will be evaluated as soon as execution flow enters. This is different in declarative languages, simply because they do not have execution flow. Or, at least, the execution flow is irrelevant. Evaluation is delayed until the point it is required. That is called lazy evaluation. And we like lazy, because lazy means CPU cycles and memory is only consumed as needed.

The best of both worlds

Languages now tend to shift to a mixture of (imperative) Object Oriented and (declarative) functional. We see that in Python, Ruby, Erlang and maybe even the best example of them all: Scala. What's even more surprising is that in PHP world, which isn't the fastest cat of the bunch, the seed is actually planted. Symfony2 introduces declarative paradigms with an inversion of control service container. That is declarative. You simply declare what service depends on another. PHP code is generated to have these dependencies resolve gracefully and lazily and we no longer need to think about execution flow for this. The services and their dependencies are only declared, and the (imperative) code for initializing the services is generated under water. Consequentally, requesting the services from the container will have the services be initialized lazily.

Another fine example is NodeJS. Since the entire model is built around one simple event loop, all behaviour is essentially defined declaratively.

So, what's next?

Learning imperative languages will become obsolete, just as learning assembly has. You will need to learn how to define behaviour and dependencies, but no longer what needs to be done to get the entire thing rolling. As languages get more and more declarative capabilities and functional programming paradigm features, the imperative idea will only be a resort where the awesomest go.

Or.... will it? Compiling code introduces overhead. There is no compiler that is able to optimize all and every situation as well as an advanced programmer could. This holds true for, say, a C to Assembly compiler, and would most certainly be the case for compiling declarative approaches. Because ultimately, the little pieces of hardware that get the work done, are not that professed in languages. They only do ones and zeros, and have no concept of meaning. This means that bringing the code from the keyboard to the processor's registry will become further and further apart. And if that's the case, there's only two ways to go. Either software will become even less efficient, or the hardware needs to get smarter. Pick one.

NB: The code in this post is on github


  1. I'm purposefully calling these "procedures" and not "functions", as functions might confuse with functions in functional programming. ↩︎


< Return to main page


You're looking at a very minimalistic archived version of this website. I wish to preserve to content, but I no longer wish to maintain Wordpress, nor handle the abuse that comes with that.