But first, what are programming paradigms? Programming paradigms are the different ways a programming language handles;
- the execution model
- code organisation and structuring
- syntax and grammar rules
Let's explore what these are.
The imperative paradigm prefers the use of statements arranged in a particular sequence, used to directly change a programs’ state. OOP groups statements together with the state they are meant to change.
The imperative paradigm also allows for the use of side effects - where a particular state can be read and set in once part of your code, at one point of time, and then later be accessed and changed by another piece of code. It is thus hard to know what code changes state when, and can be extremely hard to debug when things go wrong. Side effects can quickly make code unwieldy and unpredictable.
The OOP approach tries to limit this by encapsulating bits of state into separate objects, and only allowing the state to be changed by functions and modifiers in the same objects, and by expecting developers to follow strict best practices and accepted design patterns.
The use of inheriting classes however can often lead to ridiculous situations where you have confusing, bloated hierarchies of parent and child classes, which are hard to reason around and makes your code overly complex, less robust and brittle.
There are important differences between the classical implementation of OOP and prototypal inheritance though. OOP classes create strict hierarchical class taxonomies, with each new child class inheriting everything from it’s parent classes, in a strict linear fashion.
In prototypal inheritance, each new object created is based on a combination of any number of source objects, with no need to strictly adhere to class hierarchies, or being forced to inherit all of a parent objects’ properties. Some would say this gives all the benefits of the OOP approach while neatly side-stepping its limitations.
Functional programming, on the other hand, falls under the declarative paradigm of programming. The declarative paradigm only describes the logical components of computation, not its execution sequence or control flow.
Functional programming favors expressions over statements.
Expressions are independent combinations of input values, operators and functions, that act like mathematical functions. Given the same inputs, an expression would always return the same outputs. This makes your code predictable and consistant, and makes unit testing a lot simpler and easier, and increases confidence in your code in general.
Statements, which form part of the imperative paradigm, defines a sequence of actions or events that needs to be executed, but this execution may not necessarily return the same value every time, because changes in global state or a variable in some other class might affect its results.
Back to expressions though, the way you change variable values in the functional paradigm is to pipe inputs through a function(which is a first-class object), which then simply returns an output. There are no changes made to global states or any other variable in some other unexpected place.
The major benefit of following the functional paradigm is thus the elimination(or at least minimising) of side effects and to simplify testing implementation and code confidence.
But, if you're not careful, you could also be subjected to the downsides of both. With much freedom might come much spaghetti code.
Careful planning and consideration of design and architecture patterns thus remain very important.