Programming Paradigms: concepts and their value

Learn Powerful Programming Techniques for Problem-Solving

This post is part of our ‘The Miners’ Guide to Code Crafting’ series, designed to help aspiring developers learn and grow. Stay tuned for more!

What is a Programming Paradigm?

To start, we can define a paradigm: According to the Cambridge dictionary, a paradigm is “a model of something or an obvious and typical example of something.” We can see a paradigm as a way of resolving a problem.

A programming paradigm can be defined as a way to write code or an approach to solving a problem using a set of tools.

It’s important to say that a paradigm is a way of doing things, but not something concrete. Some languages are better at a specific paradigm than others, but a programming language is not a paradigm.

Why is it important to know about this?

Efficiency in Problem-Solving

Knowledge of programming paradigms can be key to efficiency. In the real world, there’s no recipe for everything, but some paradigms offer better approaches to solving specific problems than others. With this knowledge, you can make better decisions when creating your project.

Understanding Tools and Concepts

Another benefit is understanding specific ideas implemented in some languages, such as classes, hierarchy, or terms like functional programming. Some languages are created with a paradigm in mind, so It’s important to know more about the tool that you are using.

Cartoon-style illustration of a person looking at a bookshelf filled with colorful books.

Some of the most popular programming paradigms are:

Imperative Programming

What is Imperative Programming?

This paradigm involves giving commands to the computer in a specific order. It is older and closer to how the computer works on a basic level. The step-by-step process changes the machine’s state during each interaction.

We can understand this paradigm as a recipe: each step in a specific order produces an expected output.

Advantages and Disadvantages

This approach has some advantages because it’s simpler to understand. We generally follow this paradigm when starting to learn how to program.

However, this approach usually makes it harder to solve more complex problems and use tools like parallel programming.

In this example in C, we can see a simple program following the imperative programming paradigm:

Example in C

#include <stdio.h>

int main() {
    int sum = 0;
    for (int i = 1; i <= 10; i++) {
        sum += i;
    }
    printf("The sum of numbers from 1 to 10 is: %d\n", sum);
    return 0;
}

Object-Oriented Programming

What is Object-Oriented Programming?

Object-oriented programming (or OOP) is based on communication between objects. These objects are derived from a set of classes. In this approach, everything is an object that communicates with the other objects, making it more focused on this flow of information than on procedures.

Within this paradigm, every object has its information (properties) and can perform a set of actions (methods). OOP allows us to separate responsibility between entities better and also allows good code reusability.

Some languages, like Ruby and C++, are driven to use this paradigm. This is one of the most popular paradigms.

Key Concepts of OOP

Objects

An object is an instance of a class or an entity with characteristics and behaviors. A class is a user-defined data type with its own data and member functions (or methods). We can think of the class as a blueprint of an object, defining the properties that an object will have.

Example of an Object in Ruby:
# Class definition
class Car
  def initialize(brand, model, year)
    @brand = brand
    @model = model
    @year = year
  end

  def display_details
    puts "Car Details:"
    puts "Brand: #{@brand}"
    puts "Model: #{@model}"
    puts "Year: #{@year}"
  end
end

# Creating an object
my_car = Car.new("Toyota", "Corolla", 2020)

# Using the object to call a method
my_car.display_details

This example shows a class called Car in Ruby. It has brand, model, and year as its properties and the method display_details. When you create an instance of this class, my_car = Car.new("Toyota", "Corolla", 2020), you are creating an object.

Encapsulation

Encapsulation can be defined as wrapping data under a single unit, speaking in OOP terms, binding data and the methods together.

When deciding how to encapsulate things, we end up hiding data (information available only inside an object and making abstractions)

One common application of encapsulation is the setters and getters.

Example of Encapsulation in Ruby:
class BankAccount
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance
    @balance
  end

  def deposit(amount)
    @balance += amount if amount > 0
  end

  def withdraw(amount)
    if amount > 0 && amount <= @balance
      @balance -= amount
    else
      puts "Invalid withdrawal amount!"
    end
  end
end

The presented Ruby class is an example of encapsulation. The only way to communicate with the object is through deposit, withdrawal, and balance methods. You can not modify the balance directly or access the account owner outside the class. This is one way we control access to information.

Abstraction

Abstraction is a key concept in OOP. It’s about displaying only essential information and hiding details and implementations. An example of an abstraction could be someone using a camera app on a smartphone. The person only presses the button to capture the image, but the process of adjusting the camera and saving the picture is abstracted.

Abstractions can be used in the form of classes or even libraries. For example, when you include a math library in a given language and use a square root function, you don’t care how it is implemented.

Inheritance

Inheritance is a concept where a class inherits the behavior of another class. For example, we can create a class called ‘animal’ and then make a class called ‘cat’ based on it.

With this concept, we can extend a behavior instead of only repeating pieces of code. It also makes it easier to rewrite or overwrite behaviors for specific classes and makes organization easier.

Polymorphism

Polymorphism is another powerful concept. It allows us to define something in more than one form.

Some languages allow us to overcharge a method so that it behaves differently depending on the number of arguments. Another way of polymorphism is to redefine a method from a parent class so that the behavior of the derived class will be different.

Message

Objects communicate with each other by exchanging information through the messages.

A message is nothing more than an instruction to an object. The object will respond according to the instructions.

car.display_data

In this case, we are sending the information display_data to the object car. If a method with this name is available to the object, it will respond with the intended behavior.

Declarative Programming

What is Declarative Programming?

Declarative programming works oppositely to imperative programming. With imperative programming, we are closer to how the computer works, giving step-by-step instructions on how to do something. Meanwhile, in declarative programming, we are closer to how humans think. We are not focused on how the computer does something but more on what we need it to do.

Examples of Declarative Programming

Example in SQL

SELECT name, age
FROM employees
WHERE department = 'Engineering'
ORDER BY age DESC;

In this query on SQL, we are not concerned about how it does the database fetch it. We only specify the information we want to retrieve.

Example in Javascript

const employees = [
  { name: "Alice", department: "Engineering", age: 30 },
  { name: "Bob", department: "HR", age: 45 },
  { name: "Charlie", department: "Engineering", age: 35 }
];

const engineeringAges = employees
  .filter(employee => employee.department === "Engineering")
  .map(employee => employee.age)

In this piece of JavaScript code, we filter some information and tell the computer what we want in return. We don’t care about the process of retrieving this information.

Functional Programming

What is Functional Programming?

Functional programming is a declarative paradigm in which pure functions are used sequentially to solve complex problems. Functions receive an input and produce an output without external interference from the program.

Related Content:
A practical dive into functional programming

Key Concepts of Functional Programming

Some of the most used concepts in functional programming are recursion and immutability.

Recursion

Recursion is when the code doesn’t use conditional structures like do, while, and if-else; instead, recursive functions call themselves repeatedly until they reach a desired status.

Example of Recursion in Python:
 def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

In the example above, we have an example of recursion in Python, the function calling itself over and over until it reaches the base case.

Immutability

Immutability is when you are not able to change a variable after its creation. The reason for this is that we want to maintain the program state during execution, so each function will return the same output, which is the program state.

Example of Immutability in JavaScript:
const originalArray = [1, 2, 3, 4];

const doubledArray = originalArray.map(num => num * 2);

console.log("Original:", originalArray); // [1, 2, 3, 4]
console.log("Doubled:", doubledArray); // [2, 4, 6, 8]

In the example above, the javascript code doubles the array without changing the original value.

Pure Functions

Functional programming also uses pure functions. This type of function always returns the same output for a specific input and has no side effects, which makes debugging easier. They are also very efficient and support parallel programming.

Multi-paradigm languages

We talked about how a paradigm differs from a language, but depending on how the language was implemented, some paradigms will be more complex. Many popular languages, like Python, Ruby, and Javascript, are considered multi-paradigm. But what does that mean?

Being a multi-paradigm language means that more than one paradigm was considered when implementing it, so projects using it can benefit from approaches from more than one paradigm.

This means a language like Ruby can quickly implement OOP concepts such as classes and objects and use the step-by-step logic of imperative programming, like loops.

As a result, the tool becomes more powerful. When creating a project, there is more room for creativity and adaptability in solving problems.

Example in Python:

# Imperative
numbers = [1, 2, 3, 4, 5]
total = 0
for num in numbers:
    total += num
print(total)

# Functional
print(sum(numbers))

# OOP
class Calculator:
    def __init__(self, numbers):
        self.numbers = numbers

    def calculate_sum(self):
        return sum(self.numbers)

calc = Calculator([1, 2, 3, 4, 5])
print(calc.calculate_sum())

The example above shows the same language (python) implementing three different programming paradigms. In the real world, we often use a combination of these paradigms.

Conclusion

Paradigms are part of a developer’s toolbox.

Think about these two scenarios:

  • In this scenario, you have to design a virtual store. Using OOP would be beneficial because it would allow us to organize the code around product, client, and order classes.
  • In this other scenario, you have to manipulate a huge amount of data for scientific purposes so that a functional paradigm approach could be more efficient.

You never know when a project will explore a new direction, so basic knowledge of the main concepts is essential. With time, new paradigms emerge, such as Declarative Domain Programming and Reactive programming. Since a paradigm is a way of solving a problem, knowing about the new approach is very good and will help every developer in their journey.

Useful Links for Further Study

References

We want to work with you. Check out our "What We Do" section!