Imperative and declarative programming
In this article I am discussing the difference between imperative and declarative programming. This topic caught my attention when researching the difference between functional (FP) and object-oriented programming (OOP), as FP is a subparadigm of declarative programming, and OOP is a subparadigm of imperative.
Imperative programming is what most of us are doing in school and work. When writing imperative code, the goal is to describe all the required steps needed to accomplish the goal of the program. Simply put, imperative code answers how the task is accomplished.
Declarative programs describe what a program should do instead of explicitly describing step by step how to do it. Note that imperative and declarative code aren’t mutually exclusive, meaning that programming often involves both. Confused, yes, I was too. Allow me to explain with a real-world example and a code example.
Real-world example
Let’s say you are buying a car. An imperative description of this would go like this:
- Go to the dealership.
- Test drive some cars.
- Find a car that fits 5 people as you have a family.
- Make a deal with the owner.
- Buy the car.
Now, a declarative description would be:
- Buy a car that seats five.
See, in the imperative version, we are giving a very detailed description of how we are buying the car, while in the declarative version, we are merely declaring what we want: a car with five seats.
Code example
In this example, we want to filter out all the four-letter words from a list of words. We have two functions imperativeFilter
and declarativeFilter.
Both of the functions accomplish the same goal, which is filtering all the four letter words from a list of words. However, as we can see they reach the goal very differently. The imperativeFilter
is very specific and verbose; all necessary actions can be seen and identified. On the contrary, the declarativeFilter
is, well, declarative. It quite simply just states what it is doing while hiding the underlying actions.
void main()
{
List<String> wordList = ["Hello", "this", "is", "word", "list"];
print(imperativeFilter(wordList));
print(declarativeFilter(wordList));
}
List<String> imperativeFilter (List<String> words)
{
// We are explicitly stating how each step should happen.
// "Give me a list."
List<String> filteredWords = <String>[];
// "Go through all of the words."
for (int i = 0; i < words.length; i++)
{
// "Check if the word has four letters."
if (words[i].length == 4)
{
// "Add the word to a list."
filteredWords.add(words[i]);
}
}
// "Return."
return filteredWords;
}
List<String> declarativeFilter (List<String> words)
{
// We are declaring what we want.
// "Give me a list of words where each word has the length of four."
return words.where((word) => word.length == 4).toList();
}
Both of the paradigms have their upsides and downsides; the declarative function is more concise, faster to write and read, and a lot shorter, but it also hides a lot of the underlying logic, which makes it hard to understand if you don’t know how Dart’s built-in functions work. Meanwhile, the imperative function describes all the steps and is more generic, making it easier for others to understand, which is important in large projects where many people work together. This also helps beginner programmers, as the declarative syntax might be foreign to them.
Conclusion
In the end, both of these styles are most often utilized within a software project. This is because they are not mutually exclusive; you can and almost have to use them both at some point. Consider this example: we take the imperativeFilter
function from the last example and use it in code somewhere. This makes it instantly declarative as we do not know what is happening inside it, nor do we really care. We just want to filter the words and move on. Therefore, declarative and imperative code will always have to go hand in hand, at least to some degree.
void main()
{
List<String> wordList = ["Hello", "this", "is", "word", "list"];
// Declarative function call.
// We declare that we want filtered words, we don't exactly care how.
List<String> filteredWords = imperativeFilter(wordList);
}
List<String> imperativeFilter (List<String> words)
{
List<String> filteredWords = <String>[];
for (int i = 0; i < words.length; i++)
{
if (words[i].length == 4)
{
filteredWords.add(words[i]);
}
}
return filteredWords;
}