Programming Paradigms — Procedural, Object Oriented, and Functional Programming

These terms come up all the time but are rarely defined or discussed. So we’re going to have a look at these different ways of thinking about and writing code, and where advantages between them lie.

Matt Burgess

--

There are different ways and means to program. Some of them come up only occasionally, while some are becoming increasingly prominent. What defines these things, though, is not so much syntax or pattern as it is the way of thinking about things. These are paradigms.

The paradigms we’ll discuss are Procedural, Object Oriented, and Functional programming. As a baseline we’ll discuss these paradigms largely in JavaScript syntax, as it’s capable of all of the above, but will diverge into other languages as and when needed.

Procedural or Imperative Programming

Programming procedurally is a very explicit, step-by-step way of coding. It is fundamental and low-level, often not taking advantage of any advanced language features.

Imperative programming is the lowest common denominator. In the case of JavaScript and almost any other language you can mention, it should be avoided.

I should clarify that I use the terms imperative and procedural as synonyms here. They’re actually not. A procedural language is a type of imperative language — specifically one that supports the use of functions — aka procedures. There are relatively few examples of languages now (and certainly none I’m interested in talking about) that are imperative, but not procedural.

Let’s jump into an example to illustrate.

for (let i = 1, i <= array.length; i++) {
console.log(array[1]);
}

What this is doing should be fairly obvious. It’s looping through every element of an array. The problem with it is that it’s extremely explicit, performing the exact steps that need to be done, but not really giving the reader any clear idea what it’s doing. To clarify that, picture some code that might move a piece on a chessboard. While an imperative approach might be to move one position left, one position forward, one position left, two positions forward, one position left, other paradigms might instead do something like moveToSquare(2,B) and let the language handle the details.

In a more concrete sense, the above in a more idiomatic JavaScript sense would be this:

array.forEach(item => console.log(item));

It’s stating here what it’s doing, what the goal is, rather than explicitly stating the mechanics of doing so.

When coding imperatively, because the mechanics are often highly subject to specific details it’s not always obvious if there are bugs. In the above code using a for loop, for example, the first entry would be skipped because the array actually starts at 0, not 1. Worse still, it will also skip the end one, because it checks that the i is less than or equal to length, but the same issue exists. The last index in an array isn’t array.length but array.length — 1. Making ourselves custodian of the iterator was never a good idea because humans are a little crap at that. Or at least I am. Every goddamn time. I literally did it the other day.

As I said, procedural coding is a low-level paradigm, and that comes with significant disadvantages, but some advantages. The explicitness makes it highly performant. In fact, a lot of compilers will actually compile down to this kind of code anyway when they execute.

This isn’t as big a deal as it might sound. This kind of optimisation is often vastly less beneficial than the impact of making less readable and more error-prone code. Saving fractions of milliseconds in artificial benchmarks shouldn’t really be a goal. Especially if some sort of compiler is actually going to do that itself anyway.

Where procedural styles of programming most often come up is actually in the PHP language. Like many languages, PHP is multi-paradigm. Function calls, imperative use, procedural calls — all are often options. If we look at functionality like mysqli, for example, we can seen things like this in its documentation:

Object oriented stylemysqli::query ( string $query [, int $resultmode = MYSQLI_STORE_RESULT ] ) : mixedProcedural stylemysqli_query ( mysqli $link , string $query [, int $resultmode = MYSQLI_STORE_RESULT ] ) : mixed

Either of these will work, and will look slightly different.

$query = "SELECT first_name, last_name FROM users LIMIT 10";OOP Style$mysqli = new mysqli($host, $user, $pass, $db);
$result = $mysqli->query($query);
Procedural Style$link = mysqli_connect($host, $user, $pass, $db);
$result = mysqli_query($link, $query);

As you can see the end result isn’t all that different. But the mentality is very much so. Though this simple example doesn’t show it very well, we’ll take more of a look when we actually look at OOP.

That said, there are rare times when procedural, imperative code is the only option. The Ethereum blockchain programming language Solidity is entirely procedural, for example.

Solidity incurs an actual financial cost (called gas) with every operation, so abstractions that might make it less awful to use would also potentially be accidentally expensive. If you think about something like almost-any-language’s sort function, the amount of “stuff” that does in the background is surprisingly complex in terms of actual operations being executed. Hiding those actions behind simple-seeming functions is a bad idea.

OOP — Object Oriented Programming

This is the dominant paradigm of modern development today. JavaScript, C#, PHP, Python, Ruby, and most of all Java, the Objectest of Object Oriented Programming. It pretty much doesn’t matter what language you think of, it’s probably Object Oriented in one way or another, though the models can vary. There are some edge cases, though. Golang may or may not be an OOP language based on some pretty obscure definitions.

Definitions in the abstract are easy to find. Object Oriented Programming languages are based around core objects (data) manipulated and maintained by coded actions or methods.

In the more concrete sense, things get a little slipperier. There aren’t exact traits that are universal, beyond the fairly circular nature of being oriented around objects.

The specifics of what make an object are that they have a combination of data, and behaviours, and store, handle, and modify that data internally.

Most OOP languages are class-based. Java, PHP, and others are this kind of syntax. They often have a core file declared as something like this.

class MyClass {
firstName = "Matt";
function makeNoise() { return 'oink oink' }
}

Then they typically use something along the lines of myObject = new MyClass to create a new object based on that class. The syntax of this varies. In Ruby it’s MyClass new but the principles are the same. It should be stressed that the class isn’t an object, and the object isn’t a class, in the same way flowers aren’t seeds and houses aren’t blueprints.

JavaScript has an unusual object model. In JS the core of an object is a function called a constructor. The constructor function also has a prototype — the distinction between a prototype and a constructor is a rabbit hole I’m not going to go into. That prototype is then extended with what whatever properties and methods it needs, assuming it doesn’t start with them.

function myConstructor() {
this.firstName = "Matt";
}
myConstructor.prototype.makeNoise = function() {
return 'oink oink';
}

Exactly like above we can call const myObject = new myConstructor and it will give us an object with the data and behaviours we’d expect.

There will no doubt be a number of people reading this who are confused, and thinking that they’ve used classes in JavaScript too, and that JavaScript is also class-based. The truth is that it isn’t. Not really.

ES2015 classes are a syntactic sugar over this whole prototype thing. That’s still what’s happening internally and it’s important to recognise that. I’m not dismissing the JS class syntax, I use it pretty much exclusively myself as it’s an easier mental model, especially for those of us well versed in classes from other languages.

At this point myObject is typically an object that has data (often called properties) and actions (often called methods). The methods can be actioned by using something like myObject.makeNoise() while the properties might be something like myObject.firstName.

This combination of properties (data) and methods (behaviour) is a fundamental principle of OOP. It means that an object knows things, and knows what to do about it.

One of the other common traits is some form of inheritance. This means that you can take a base concept and extend it to more specifics. Take animal and extend it to cat. Take a cat and extend it to leopard. Not exactly a real-world example, but something like useradmineditor probably makes more sense.

Object Oriented Programming has significant real-world benefits. It’s a very easy mental model, that is very clearly explainable. It takes examples and analogies well. Simple examples in a few lines make sense.

class Publication {
Date lastRented;
Date returned;
Date publishDate;
String publisher;
function rent() { /* whatever this does */ } function getAvailability() { /* whatever this does */}
}
class Book extends Publication {
String isbn;
Integer pageCount;
}
class Novel extends Book {
String title;
String author;
String genre;
}
class NonFiction extends Book {
String deweyDecimal;
String title;
String subTitle;
}
class Newspaper extends Publication {
String Headline;
}

That’s not written in any specific language (kind of TypeScripty, I guess) but it makes general sense.

ORMs especially make good examples of the benefits of object-based thinking. Active Record based ORMs translates each row in a database to a single object. This is the PHP framework Laravel’s ORM called Eloquent.

$user = User::find(4);
echo $user->first_name; // Steve
$user->first_name = 'Sam';
$user->save();

You don’t really need to know PHP, Laravel or Eloquent to follow what that’s doing. The mental model makes sense, and is intuitive to use. We also see examples here of the fact that an object has data (first_name in this case but we could assume last_name, email, etc) and methods. Only save() is used here but there are loads of others. This is also an example of real-world inheritance. One of the first lines of our User class will be class User extends Model, where Model is coming in from Laravel itself.

Again, PHP is an example of an object oriented language and we can compare its procedural operations with its OOP implementations of the same functions; taking a mysqli prepared statement, for example.

$query = "SELECT name, abbreviation FROM states WHERE country_code = ?";OOP Style$statement = $mysqli->prepare($query);
$statement->bind_param("s", $country_code);
$statement->execute();
$result = $statement->get_result();
Procedural Style$statement = mysqli_stmt_init($link);
mysqli_stmt_prepare($statement, $query);
mysqli_stmt_bind_param($statement, "s", $country_code);
mysqli_stmt_execute($statement);
$result = mysqli_stmt_get_result($statement);

You’ll notice a few things here. One is that the procedural style has an extra step, because it needs to say what database connection to act on, where the OOP version is by definition acting on the previously created object. The other is that function names in the procedural style get reaaaaal long. This is because OOP is kind of automatically self-scoping, where the procedural code has to be very explicit in order to namespace itself clearly.

There is another benefit here that’s a bit less clear from these examples. One thing that makes OOP so useful is that objects are a first class citizen. This means that objects can (and should) be happily passed around from methods in one object to another. These richer, more expressive objects can encapsulate behaviour and logic and make the overall experience a lot cleaner. I’ve written quite a bit on how (and why) to use OOP specifically in PHP, and I won’t labour that point.

To conclude, OOP doesn’t really need defending. It’s the dominant paradigm of most common programming languages.

Functional Programming

FP is probably the biggest buzzword in terms of paradigms. While OOP has the lion’s share of actual work, a lot of newer languages are more functional, and multi-paradigm languages like JS are supporting more functional ideas.

In the same way Java is kind of the epitome of OOP, the definitive Functional Programming language is probably Haskell. Many others exist, though. Lisp, Elixir, F#, and others. Elm is a functional language that compiles to JavaScript, and combines some beautifully written error handling and architecture with the single worst templating model I’ve ever seen.

We already talked about how Procedural Coding is function based, so how is that different to functional programming?

The difference is a little abstract, but the code above for procedural mysqli in php gives a good example.

$statement = mysqli_stmt_init($link);
mysqli_stmt_prepare($statement, $query);
mysqli_stmt_bind_param($statement, "s", $country_code);
mysqli_stmt_execute($statement);
$result = mysqli_stmt_get_result($statement);

Note that in the second line, the mysqli_stmt_prepare function is passed a $statement which it then “does something” to. That “does something” is referred to as a state change. In this context it’s a side-effect.

By contrast, a functional language usually focuses on pure functions. A pure function is one where the same input will always return the same output. There’s nothing occurring in the function that can have any effect anywhere else in the system, and no other information is used.

// pure function
function double(int number) {
return number * 2;
}

This is a pure function, because it just returns the number after performing the action, it doesn’t get any information from anywhere else, and doesn’t do anything else.

//impure functions
function calculateTax(int amount) {
int taxRate = User.stateTaxRate;
return amount * (taxRate / 100);
}
function updateTaxRate(int amount) {
User.stateTaxRate = amount;
}

The first one is not a pure function, because it uses data that isn’t passed in. The second isn’t pure because it makes changes elsewhere in the system. I should note that it doesn’t matter how many arguments there are (called arity).

Pure functions are good because they just have an input and an output and don’t do anything spooky in the middle.

Certain languages are either partially or completely functional. JavaScript is one. I’ve written previously about JavaScript as a functional language, and I don’t find it particularly impressive as one. Too much of its core functionality is object based, meaning a lot of not-particularly-intuitive workarounds to make it pretend to be functional. Or alternatively implementation of libraries like Ramda.

Languages like Elixir are much more interesting to talk about in terms of functional programming.

Elixir uses a very different syntax to define functions than something C-based, it’s actually (by design) quite a lot like Ruby.

defmodule MyModule do  def create_date_pair(line) do
line
|> String.replace("\r", "")
|> String.replace("\n", "")
|> String.split(",")
|> List.to_tuple
end
def write_map_to_file(map, filename) do
list = Enum.map(map, fn {k, v} -> "#{k},#{v}" end)
|> Enum.join("\n")
File.write(filename, list)
map
end
end

This is stolen from some of my own code for a small proof of concept I did. I chose these chunks from a larger whole to illustrate a few things. First of all, the second function, write_map_to_file is absolutely not a pure function. I didn’t want to make it seem like impure functions or side effect weren’t possible within functional languages. They absolutely are necessary for real working software. They just should be isolated to make it clear what they do. Note also that this function takes in a map and then returns that map. Actually that’s not so clear. Elixir has an implicit return, the last line is automatically returned. This is why the map is put at the end of the second function. The first function just returns the entire context.

The second thing these code blocks illustrate is that because Elixir is entirely focused on functions it has a lot of shorthands and features to better handle passing and using functions.

In the top example that |> is a pipeline operator, which passes the return of one function to the first argument of the next function. Which can lead to some really elegant functional code.

{:ok, formatted_value} = difference 
|> trunc
|> abs
|> NumberFormat.to_string

There are also shortcuts like fn {k, v} -> "#{k},#{v}" end which is not unlike a fat arrow function in JavaScript, and even more abbreviated with something like &(&1 * &2), which I think is… valid. I’m no expert with Elixir. It essentially makes an anonymous function where the first and second argument are multiplied together.

Elixir allows things like overloading, where functions are completely different based on how many arguments are passed in, and can be defined with guards, such that these work.

def sum(a, b) when is_integer(a) and is_integer(b) do
a + b
end

def sum(a, b) when is_list(a) and is_list(b) do
a ++ b
end

Though seemingly the same function the guards on the definition limit their usage. This is a very powerful way of handling type differences or potential errors or edge cases. The ++ operator just combines two lists, by the way.

Elixir is not the only way to do functional programming of course. Microsoft offers F#, which is relatively similar in syntax and intent.

JavaScript can be functional as well, and more so when certain libraries are used. An example is Ramda, which adds a bunch of functional approaches, though it’s obsessed with currying and function composition which IMO makes it far less user friendly. Or maybe I’m just thick.

Most functional programming languages are frankly very advanced, and often relatively new. It’s a paradigm that’s seen a rapid rise only in the last five to ten years, by comparison to OOPs dominance for decades.

There is a certain amount of conflation that occurs. That many FP languages are exceptional is completely true. But whether they’re exceptional because they’re functional is an often begged question. Similarly many are extremely high performance, but are they high performance because they’re functional? Many have excellent error handling, but is that because of functional programming, or just good design?

In part this is because functional languages benefit from something called the Late Mover Advantage. By seeing the mistakes and issues in their forerunners they’re able to avoid or counter those problems. They’re able to come in with a solution that starts off more mature and reliable. The sorts of excellent and powerful features they tout as benefits of functional programming might also be found in a brand new Object Oriented language, for example. In short, it’s difficult to split out the benefits of a given language with the benefits of its paradigm.

Claims about functional programming paradigms are often made with the assumption they’ll be accepted at face value. That they’re less prone to bugs, faster to write, faster to execute, easier to understand, etc, are all claims routinely made without demonstration or evidence. Other arguments are poorly made. I have previously mentioned another article comparing functional with object oriented programming whose “OOP” examples were actually badly written procedural programming.

None of this is really a criticism of FP in general. I’m a big fan of Elixir, though more for its language features than that it offers functional programming. The fact is, functional programming as a paradigm is here to stay.

--

--

Matt Burgess

Senior Web Developer based in Bangkok, Thailand. Javascript, Web and Blockchain Developer.