Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: March 18, 2024
Programming paradigms are models employed for developing computational solutions to problems. These paradigms establish the rules and logic to develop programs. In this way, programming languages implement these rules and logic, providing them for programmers. Currently, most existing programming languages support the imperative paradigm, the declarative paradigm, or both.
In this article, we’ll explore the imperative and declarative programming paradigms. First, we’ll have a brief review of programming paradigms. Then, we’ll investigate the imperative paradigm. In this context, we’ll analyze the derived paradigms of procedural and object-oriented programming. Next, we’ll inspect the declarative paradigm and its derived paradigms of functional and logic programming. Finally, we’ll review and compare the programming paradigms into a systematic summary.
As a result of computer programming, human beings could improve their performance in executing several processes. There are various methods to program computers. However, regardless of the adopted method, the programmer must communicate with the computers through a specific language.
This language can be machine language, which considers only binary symbols (0 and 1). But, writing programs using only binary symbols is pretty unusual and difficult for modern humans that employ complex and rich language systems. So, in order to overcome these difficulties, many programming languages with particular lexicon, syntax, and semantics were proposed over the years.
The programming languages subdivide into paradigms according to their features. Examples of these features are the code structures (how the programmers should write the code) and the execution models (how the computers proceed with the program execution). Currently, there exist two major programming paradigms: imperative and declarative. These paradigms, in turn, subdivide into other paradigms with even more particular characteristics.
In the following sections, we’ll explore the main characteristics of the most prominent programming paradigms and understand how they work.
The imperative paradigm is the oldest computer programming paradigm. This paradigm presents as a central characteristic the definition of sequences of instructions representing modifications in the states of a computer system. Thus, we can see programs following the imperative paradigm as “guides” that describe how to accomplish some task.
The imperative paradigm has several references in von Neumann’s architecture. This architecture takes into account the use of a processing unit, a control unit, and memory. In summary, the processing unit consists of an arithmetic module and processor registers, and the control unit has instruction registers and a program counter. The memory, in turn, keeps data and instructions.
Imperative programming languages mimes strategies presented in the context of von Neumann’s architecture. For example, the memory stores data and instructions of a program. Thus, statements are employed to manipulate the data in the memory, such as assigning it to variables or executing arithmetic operations. There is a sequential order for executing instructions of programs. So, a special variable, the instruction pointer, indicates the next program instruction to execute. At last, conditionals, loops, and other control commands, such as goto, can modify programs’ execution order, changing the value of the instruction pointer.
The imperative paradigm has two popular derived paradigms: procedural and object-oriented. In the following subsections, we’ll see some relevant concepts about them.
The procedural programming paradigm focuses on subdividing a program from a simple sequence of instructions to a collection of subroutines with particular instructions, structures, and variables. Prior to procedural programming, the repetition of a specific portion of code relied on using goto statements. However, it made program codes very complex to follow and hard to maintain and update. Thus, the partition of a monolithic code into subroutines promoted by procedural programming significantly improved the modularity of program codes.
A subroutine of a program is tailored to accomplish a single well-defined task. However, implementing subroutines demanded new instructions for developing programs:
The following image presents an example of non-procedural and procedural equivalent programs to sum or subtract two numbers:
Examples of programming languages that support procedural programming are Basic, C, Pascal, and Python.
After the procedural programming paradigm, the imperative paradigm got two sequential evolutions: structured programming and modular programming. First, structured programming defined that a subroutine has a single starting command and, preferably, a single exit point. So, modular programming divided programs into modules with particular members: names, procedures, variables, and submodules.
The object-oriented programming paradigm was inspired by concepts from modular programming. However, the object-oriented paradigm aims to represent the real world, modeling it similarly to our brains. So, this paradigm leads new meanings and properties to concepts from modular programming, remodeling them. Examples of these remodeled concepts are classes, attributes, and methods:
The following image highlights the previously presented concepts in a concrete example:
Furthermore, object-oriented programming defined new concepts for the imperative paradigm, such as abstraction, encapsulation, inheritance, and polymorphism.
Examples of programming languages that support object-oriented programming are C++, PHP, Java, and Python.
The declarative paradigm has as the main feature the definition of tasks that computer systems must accomplish. Different from the imperative paradigm, the declarative paradigm doesn’t make clear how to process a task. On the contrary, programs following the declarative paradigm work as “guides” with instructions defining what tasks a computer system should accomplish, regardless of how these tasks are accomplished.
Due to this characteristic of describing what to do instead of how to do something, program codes in the declarative paradigm naturally pose a higher abstraction level. It is a consequence of using predetermined command structures to define tasks. This higher abstraction level also improves the maintainability of a program due to the standardized operation of these command structures. Thus, it is easier for different teams to work on distinct stages of a software lifecycle, such as development and maintenance.
Two popular paradigms derive from the declarative paradigm: functional and logic. We’ll study these paradigms in the following subsections.
The functional paradigm considers that programs are compositions of multiple functions. In such a scenario, we can employ functions as any data type. Thus, functions can have identifiers and work as arguments or results of other functions. Furthermore, there are different categories of functions, each one considering particular characteristics:
The following image shows a practical example of the described functions:
Finally, an interesting aspect of the functional paradigm is that variables aren’t appreciated. In this way, data should never change (immutability property). So, for example, to add a new value to a list, we should destroy the old list and create a new one with the same values plus the new value.
Examples of programming languages that support functional programming are Haskell, Scala, and Kotlin.
The logic programming paradigm uses formal logic to solve a myriad of problems. This paradigm relies on a knowledge base with several facts and rules to answer queries:
With the described resources, programs use an inference system to answer queries. So, the inference system searches for proofs for each query. Finally, the program returns the deductible solutions to a query. Otherwise, if there are no solutions, the program returns a false value.
The following image demonstrates the previously presented concepts:
Examples of programming languages that support logic programming are ASP, Datalog, and Prolog.
The imperative and declarative paradigms have differences between them. Making an analogy, consider that your car has broken. So, you can follow a step-by-step tutorial to fix it by yourself (imperative solution) or ask a mechanic to solve the problem (declarative solution). Without extra information about the context of the problem, it is not possible to say which alternative is better. The same occurs for programming paradigms.
The imperative and declarative paradigms, however, have comparable features, advantages, and disadvantages. The following table summarizes some of them:
| Paradigm | Imperative | Declarative |
|---|---|---|
| Operation | Defines HOW tasks should be accomplished | Defines WHAT tasks should be accomplished |
| Computation | Defines the control flow and states changes | Defines only the logic |
| Mutating Variables | Very usual | Unusual and not recommended |
| Advantages | Easy to learn notation; Machine architecture compliant | Easy to optimize codes; High abstraction level |
| Disadvantages | Hard debugging; Vulnerable to data race | Unfamiliar notation; Less customizable codes |
| Popular Derived Paradigms | Procedural; Object-oriented | Functional; Logic |
In this article, we learned about imperative and declarative programming paradigms. First, we studied the imperative paradigm, defining its main characteristics and investigating the derived paradigms of procedural and object-oriented programming. So, we similarly studied the declarative paradigm, especially investigating the derived paradigms of functional and logic programming. Finally, we put both the imperative and declarative paradigms face to face into a systematic summary.
We can conclude that the existence of different programming paradigms is essential for developing better programs. In this way, programmers can choose their strategies and tools that best fit the problems to be solved.