Programming Paradigms

An intro to modern programming paradigms.

We'll cover the following

Bash is a procedural programming language. Procedural languages allow us to divide a program into logical parts called subroutines. A subroutine is an independent block of code that solves a specific task. A program calls subroutines when it is necessary.

A subroutine is a deprecated term. It is called a function in modern programming languages. We already came across functions when considering the declare Bash built-in. Now, it is time to study them in detail.

Functions

We should start with the terminology. It will explain to us why functions were introduced and which tasks they solve.

But, what is procedural programming? It is one of the paradigms of software development. A paradigm is a set of ideas, methods, and principles that define how to write programs.

There are two dominant paradigms today. Most modern programming languages follow them. These paradigms are the following:

  1. Imperative programming. The developer explicitly specifies to the computer how to change the state of the program. In other words, they write a complete algorithm to calculate results.

  2. Declarative programming. The developer specifies the properties of the desired result, but not the algorithm to calculate it.

Bash follows the first paradigm. It is an imperative language.

The imperative and declarative paradigms define general principles for writing programs. There are different methodologies or approaches within the same paradigm. Each methodology offers specific programming techniques.

The imperative paradigm has two dominant methodologies:

  1. Procedural programming.

  2. Object-oriented programming.

Each of these methodologies suggests a specific way for structuring the source code of programs. Bash follows the first methodology.

Procedural programming

Let’s take a closer look at procedural programming. This methodology suggests features for combining the program instructions into independent code blocks. These blocks are called subroutines or functions.

We can call a function from any point within a program. The function can receive input parameters. This mechanism works similarly to passing command-line parameters to a script. This is a reason why a function is sometimes called “a program inside a program.”

The main task of the functions is to manage the complexity of the source code. The bigger a code is, the harder it is to maintain. Repeating code fragments makes things worse. They are scattered throughout the program and may contain errors. After fixing a mistake in one fragment, we have to find and fix all the rest. If we put the fragment into a function, it is enough to fix the error only there.

Here is an example of a repeating code fragment. Let’s suppose that we write a large program. Whenever an error happens, the program prints the corresponding text message to the error stream. This approach leads to duplicating echo calls in the source code. The typical call looks like this:

>&2 echo "The N error has happened"

At some point, we decide that it is better to write all errors to a log file. It will help us to debug possible issues. Users of our program may redirect the error stream to the log file themselves. This is a good idea, but some users do not know how to use redirection. Thus, our program must write messages into the log file by itself.

Let’s change the way the program prints error messages. This means that we need to check every place where it happens. We should change the echo calls there like this:

echo "The N error has happened" >> debug.log

If we miss one echo call accidentally, its output does not come to the log file. This specific output can be critical for debugging. Without it, we would not understand why the program fails on the user side.

We considered one of several problems of maintaining programs. The maintenance forces us to change the existing source code. If we violate the Don’t-Repeat-Yourself (DRY) development principle, we get in a lot of trouble. We must remember a simple rule: we do not copy the same code block of our program.

Functions solve the problem of code duplication. They resemble loops in some sense. The difference is, a loop executes a code block in one place of the program cyclically. In contrast to a loop, a function executes the code block at different program places.

Using functions improves the readability of the source code. A function combines a set of commands into a single block. If we assign a descriptive name to this block, it’ll be more obvious what the block does. Then, we can use this name to call the function. It makes our program easier to read. We replace a dozen lines of the function body with its name wherever we call it.

Get hands-on with 1200+ tech skills courses.