Programming languages come in all shapes and sizes: interpreted vs. compiled, weak vs. strong typing, low-level vs. high-level, terse vs. expressive… There are many buckets you can put a programming language into, even though not all are equally meaningful.
One very common way people classify languages is to organize them into paradigms. You can think of a paradigm as a group of languages that share similar characteristics. There are many paradigms currently in use: procedural, functional, and object-oriented. Many of these terms are often misused or confused; there’s also some degree of overlap between different paradigms, which definitely doesn’t make things easier.
Add all of that together and what you get is a landscape that’s not too easy for a beginner to grasp. In today’s post, we’ll try and fix this situation by giving you a clear picture of the imperative programming paradigm.
You Know What Programming Is: What About “Imperative”?
If you’re reading this post, chances are that you’re a software developer, in which case it’d be safe to assume that you know what programming is. Now we need to decipher whatever “imperative” means. And for that, we’re going to ask you to recall your English classes.
Did you ever study the imperative mood? If the answer is no, here goes a quick reminder, courtesy of your friendly neighborhood Wikipedia:
The imperative mood is a grammatical moodthat forms a command or request.
Every time you ask someone to do something, you’re using the imperative mood. But what does all of that have to do with programming? Well, everything.
Under the imperative programming paradigm, you program by giving the computer orders or commands. You tell it exactly what it needs to do, how it needs to do it, and in what exact order. This is opposed to, let’s say, declarative programming, in which you declare the end result without specifying the dirty details needed to get there.
Imperative Programming: Why Is It Useful? The Human Reason
We’ve just seen a basic definition of imperative programming. Now, let’s build on that definition to gain a better understanding. With the “what” out of the way, let’s focus on the “why.” Why is imperative programming a thing?
Well, try to think about the things you say when you’re giving someone directions:
- Go ahead two blocks, then turn right;
- After three blocks, turn left;
- Walk for more one block and you’ll get to the library: it’ll be the huge yellow building to your right!
It’s not that hard to notice that you’re using the imperative mood, right? You’re issuing more instructions than commands, of course, but it’s imperative nonetheless.
Now think about the recipe for your favorite dish. For instance, what follows is the beginning of a recipe I’ve just found online:
- Weigh the ingredients for the dough into a large bowl and add 1/2 tsp salt and 125ml warm water.
- Mix to form a soft dough, then tip onto your work surface and knead for 5 mins or until the dough feels stretchy.
- Clean and grease the bowl and return the dough.
- Cover with cling film and leave somewhere warm to rise for 1 hr, or until the dough has doubled in size.
Again, notice how each step of the recipe is a phrase as a command or instruction. That’s one of the reasons why the imperative paradigm is so pervasive in programming. It translates very naturally to the way we humans are used to approaching problems—i.e., breaking them into a series of sequential steps.
Imperative Programming: Why Is It Useful? The Machine Reason
We’ve just seen the human rationale for imperative programming like breaking down problems into linear steps because it’s something we all do constantly. Let’s now analyze the technological rationale.
Here we have, again, a quote from Wikipedia:
The hardware implementation of almost all computers is imperative.Nearly all computer hardware is designed to execute machine code, which is native to the computer and is written in the imperative style.
That’s pretty much all there is to it: computation itself is, at its lowest levels, imperative. Each new generation of programming languages, tools, and artifacts build new layers of abstractions. But no matter how high these layers get, at some point, it all gets down to the processor executing instructions.
The Pillars of Imperative Programming: Sequence and Mutability
We’ve already given you a definition of imperative programming. We’ve also talked about its usefulness in programming and why it’s such a profoundly common approach to problem-solving, citing both the human and technological aspects.
Now let’s get our hands dirty and talk about how imperative programming characteristics play out in code. As it turns out, two of the most important pillars of imperative programming are sequential execution and mutable state.
Sequential Execution
As we’ve mentioned earlier, imperative programming translates very well into the way we naturally deal with problems by breaking them into discrete steps that we handle in a specific order. How does that play out with code, then? Consider the following snippet:
1 2 3 |
Console.WriteLine(“What’s in your age in years?”); var age = int.Parse(Console.ReadLine()); Console.WriteLine(“You’re {0} years old”, age); |
Here we have three lines of a very straightforward code. In the first line, we use the “Console.WriteLine” static method to print a message, asking the user their age.
Right after that, we use the “ReadLine” static method to read the user’s input. We then pass it as an argument to the “int.Parse()” static method that converts it to an integer. Finally, we assign the result of this parsing to the “age” variable.
In the end, we print a message, informing the user of their age (that they’ve just typed). Not the most useful program on Earth, I admit, but it fits our purposes.
It demonstrates, very clearly, the sequential nature of the imperative paradigm. The lines above have to be at that exact order if our program is to have the desired behavior. I couldn’t use the “age” variable before it’s declared and receives some value. I could read from the user’s input and convert it to a number before I showed them the message asking for their age…but that would make for a poor user experience.
Mutable State
Let’s say we’ve made some changes to our previous example. Now it reads like this:
1 2 3 4 5 |
Console.WriteLine(“What’s in your age in years?”); var age = int.Parse(Console.ReadLine()); Console.WriteLine(“You’re {0} years old”, age); age++; Console.WriteLine(“One year has passed. Now you’re {0} years old”, age); |
The new version of the code has two additional lines. Now, right after showing the message, we increment the age variable by one. We then show the user a new message, stating that they’re now a year older.
Again, not a useful program, but it nicely illustrates one of the cornerstones of the imperative programming paradigm: mutable values. You can think of declaring a variable as allocating a space in memory and assigning a label to that space. You then get to read and write to that location.
Nowadays, we developers tend to sing immutability praises whenever we have someone around who’ll listen—been there, done that myself. And make no mistake: designing with immutability in mind can be a powerful approach to programming, making your code cleaner and easier to reason about.
But some domains are probably more well-suited to mutability. Just like we are used to breaking problems into sequential steps, we’re also accustomed to seeing things that around us change their state all of the time. If only my car would have an immutable fuel tank that kept full all the time!
The Invisible Paradigm
Have you ever heard the phrase, “Most people don’t use an operating system; they use applications”? The idea here is that if your OS is doing its job well, you shouldn’t even notice most of the time. It’s only when it fails that you become aware of its existence again.
I defend that the same is true for programming paradigms. When you’re writing an app in C# (or Java, Python, whatever) you shouldn’t really be aware of whatever paradigm that language defends. When you’re writing code, you want to get things done and that’s it.
It’s only when it gets in your way that you became aware of the programming paradigm again. Add to that the fact the many of today’s major languages play very loosely with paradigms. They pick and choose features from a large pool containing offerings by many paradigms in order to offer you a tool as flexible and powerful as possible.