The shift of thinking: Imperative vs. declarative programming

Posted on Mar 4, 2023

A key characteristic of functional programming is the declarative way of describing how software shall behave.

In contrast to that stands the imperative style, where you give step-by-step instructions what the program shall do.

Von Neumann Architecture

The reason why the imperative style lasts so long and is so old, lays in the way how computers work.

The basis of our computers til today is the von Neumann architecture.

In the von Neumann architecture the computer gets instructions which tell it how it should work. So the algorithm is imperative and tells which data should be loaded and which instruction shall be executed.

This is the reason, why the imperative programming style lasted so long and is so old!

Example for imperative style

Look at the following Java sample, which calculates the sum of all even numbers in an array and prints the result:

public static void printSumOfEvenNumbers() {
    int[] arr = new int[]{1,2,5,10,11,23,27,30};

    int result = 0;
    for (int i = 0; i <= arr.length-1; i++) {
        if ((arr[i] % 2) == 0) {
            result += arr[i];
        }
    }
    System.out.println(result); // = 42
}

You would read the program something like the following: ‘Declare a variable result with the value zero. Iterate through every value of arr. If the current value is even, add it to result. Print the result afterwards.’

You can see and feel the loading and storing of data (= assignments) and the instructions (= loops, conditions) given to it. This is exactly telling the computer what to do!

The declarative style

The functional style hides this behavior in functions. You declare which result you want to get:

public static void printSumOfEvenNumbers() {
    int[] arr = new int[]{1,2,5,10,11,23,27,30};

    int res = Arrays.stream(arr)
            .filter(value -> value % 2 == 0)
            .sum();
    System.out.println(res);
}

You would read the program like following:

‘Filter every value which is even and calculate the sum. Print the result ‘. You see, it is more natural and more compact.

An even more declarative way

Even when the implementation from above is already very compact, you can work further on it and give it an even more declarative way:

public static void printSumOfEvenNumbers() {
    int[] arr = new int[]{1,2,5,10,11,23,27,30};
    
    sumOfEvenNumbers
        .andThen(print)
        .apply(arr);
}

The real implementation is in their own functions:

static Function<Integer, Void> print = val -> {
        System.out.println(val);
        return null;
};

static Function<int[], Integer> sumOfEvenNumbers = arr -> Arrays.stream(arr)
    .filter(value -> value % 2 == 0)
    .sum();

Conclusion

This post outlined the differences of the imperative and declarative programming style.

We saw how and why the imperative style was created and why it looks like giving the computer instructions.

The declarative style is more verbose.

Not only that this splits the problem up in small steps, you also have small chunks which work well together and feel more natural when reasoning about it.

However, using the declarative way can first feel a little bit different when using the first time. But I think when you get used to it, it is a good and fun way to solve problems.

Further reading

If you want to learn more about functional programming and the declarative way of thinking and solving problems, then I do recommend the book The Art of Functional Programming by Minh Quang Tran.

It is a good and compact book which shows the ideas behind the building blocks of functional programming.

Want to know more?

Keep on reading and choose one of the related articles. You can also check the home page for my latest thoughts, notes and articles.