Full-Stack Engine

Javascript Paradigms: Functional

As a Javascript developer, once you've spent some time in this language, you might start wondering what's the right approach to solve problems, especially taking into account the many options at your disposal. You may go with OOP using ES6 classes, inheritance and all the design patterns that have evolved during the past 20 years, all the traditional way, as we might expect from Java, C++, C#, and others. Alternatively, maybe you might still use OOP in the pure essence of Javascript, with closures, function constructors, prototypical inheritance, Object notations, and several others.

However, you can also write code in a functional programming style, with Pure Functions, First Class Functions, callbacks, function generators and others.

If you are not familiar with some of those concepts, don't sweat it. It happened to me too, at the beginning I found all of that confusing and in some extend overwhelming. Perhaps it's because they don't teach you that in the college or on many Programming Courses. One possible reason is the fact that JS is so versatile, flexible and rapidly evolving, that it was not a suitable language for beginners nor specialized in a particular paradigm (we'll cover the essential paradigms in short). Even if there's a course that tries to cover all the most critical parts of Javascript, it can be quickly outdated in less than five years!.

Programming Paradigms

Instead of covering the essential features in Javascript or the most relevant frameworks in this 2018 (if you are interested in frameworks, take a look at this article), this time I want to make a different approach by reviewing how we can use JavaScript with the many paradigms it supports. Passing through the programming principles, like the SOLID principles or Design Patterns and learning what the best paradigm for the kind of problem is. For example, functional programming is a better approach to deal with concurrent calls that access or modify the application state, that's why state containers like Redux are implemented with pure functions and never writing directly to the state, but requesting a change by actions.

Let's start by defining what a Programming Paradigm is. I like the definition Robert C. Martin gives in his book Clean Architecture: A programming paradigm is a way to provide discipline to us, as developers, over one of three main aspects or affairs: Direct transfer of control (structured programming), indirect transfer of control (or memory allocation, Object Oriented Programming) and memory assignment or modification (Functional Programming).

There three fundamental paradigms work by limiting in some way the freedom we have to program a computer, but why?; well, its inherent in what we are and how our brain works, no one can create or design a thing without any guidelines. Think about an artist trying to create a painting, she would probably need to start with some basic sketch with lines and shapes and then decide what kind of colors to use, or the paintbrush, oil paint or pastel. Even if she wants to create an abstract painting, is still limited by the canvas type, the paint, brushes, and others instruments. Programming is not so different, as it is a craftsman work too.

Functional Programming

In this article, I want to start with the first paradigm created: Functional Programming. Besides of being the first one, was the last to gain popularity among the different languages nowadays, perhaps by its strong mathematical background or maybe is not so straightforward for beginners as OOP or structured programming. However, it's proved its worth in our skill set, as we can solve common, nontrivial problems naturally and cleanly with FP.

As I cited from Uncle Bob before, Functional Programming is a paradigm which imposes discipline upon assignment, meaning that a Functional language or coding style makes it hard for us to modify any memory location, like variables, forcing us to use only functions for any task we do.

FP Origins

Functional programming has its origins in lambda calculus or $\lambda$-calculus, a formal mathematical system developed in the 1930s for computing research related fields.
In the very beginnings of Programming and Computer Science, only scientific and mathematicians could write code that a computer system could understand. Because at the time programmers didn't have the hardware capabilities we have today, like a large memory to store and execute programs or high-speed processors that can run IDEs and compilers. Instead, they had something that we could hardly recognize as a computer, so basic that the only way to build a useful program had advanced skills in mathematics and electronics.

As we'll see in detail, in FP everything we do is done with a function, there are no state or variables in memory; something useful in the beginnings of computing as the memory was something costly and slow at the time, but also helpful today when building concurrent systems. The main issue faced when writing a concurrent program is having shared sections of memory trying to be accessed (read and write) by several processes simultaneously, generating race conditions and every kind of weird bugs if not handled carefully. However, what happens if we don't have any shared state or any internal state at all? Then there can't be any concurrency problems.

Let's get more rooted in the components of Functional Programming.

Immutability

Immutability plays an essential role in Functional and Object Oriented Programming and refers to the property of an object to remain unchanged after its creation, which is in contrast to a mutable object, modifiable after its creation.

Functional Programming, as mentioned before, imposes discipline upon assignment, forcing us not changing state directly, or mutating objects; instead, if we need to change the state, then use a function (pure) especially for the task.

The benefit of immutability is evident when using concurrency. An immutable object is inherently thread-safe. On the other hand, if we don't modify state directly, but instead calculate the next state with pure functions, then we don't have race conditions; that's the essential idea behind of state containers as stores in Redux.

Nevertheless, JavaScript is not a native FP language, nor it has immutability enforced in its core, but it offers the flexibility to implement, only we need to use some specialized library like Immutable.js or Mori.

Pure Functions

Pure functions are a particular kind of function that satisfies the following conditions:

  1. Given the same inputs, it always returns the same output
  2. It has no side effects

Side Effects

A side effect is the modification of some state outside its local environment or has an observable interaction with the outside world, besides its returning value, something familiar in methods inside an object (OOP) or reading/writing to global objects (window object in JavaScript).

When we isolated side effects from our program logic, code is much easier to test, extend, refactor, debug and maintain.

Function Composition

In mathematics, function composition is the process of creating new functions taking the result of a single function and passing it as an input of another function.
A formal definition can be written in the following example,

Given the functions $f: X \rightarrow{} Y$ and $g: Y \rightarrow Z$, a third function $h$ can be composed $g(f(x))$ so that,

$$h: g(f(x)): X \rightarrow Z $$

Function composition can be done straightforwardly in JavaScript, thanks to the First Class functions, which allows us to treat functions as data and pass it as function arguments.

Higher-order functions

Higher-order functions are functions that do at least one of the following:

  • Takes one or more functions are arguments
  • Returns a function as its result

Typical uses of Higher-order functions include:

  • Create utilities which work on a wide variety of data types
  • Partially apply a function to its arguments or created a curried function to reuse or for function composition.
  • Take a list of functions and return some composition of those input functions
  • Isolate or abstract actions, effects or async flow control using callback functions, promises and others [1]

Partial application

Partial function application refers to the process of fixing some arguments to a function, producing a smaller one. Given a function $f:(X, Y, Z) \rightarrow N$, we might fix the first argument, producing function $partial(f):(Y,Z) \rightarrow N$.

In JavaScript we can find common use of Partial Application with function.bind(), where besided providing a value for this, we can also provide values for any number of arguments in a function, following this syntax:

function.bind(thisArg[, arg1[, arg2[, ...]]])

For example, in the following code snippet, add function can be simplified from taking two arguments to have the fixed argument fixed, giving; as a result of an add function that takes only one argument:

let add = (a, b) => a+b;

let increment = add.bind(null,1);
let incrementBy2 = add.bind(null,2);

console.log('Increment 3 by 2:',incrementBy2(3));
//=> Increment 3 by 2: 5
console.log('Increment 3 by 1:',increment(3));
//=> Increment 3 by 1: 4

Currying

Currying, on the other hand, is a technique related to Partial Application, where a function that takes multiple arguments translated into a function that does the same task in a sequence of simpler functions.

Let's consider the previous increment example with Partial Application, but doing it now with Currying:

let add = x => y => x+y;

let increment = add(1);
let incrementBy2 = add(2);

console.log('Increment 3 by 1:',increment(3));
//=> Increment 3 by 1: 4
console.log('Increment 3 by 2:',incrementBy2(3));
//=> Increment 3 by 2: 5

As you can see, Currying works with higher order functions; in this example when we call add, it returns a partially applied function with the fixed argument provided.

We can also use a curried function in a single sequence, for example:

function sum3(x) {
  return (y) => {
    return (z) => {
      return x + y + z;
    };
  };
}
console.log(sum3(1)(2)(3)) // 6

That's the way react-router connects a React component with Redux,

class MyComponent extends Component {
...
}

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

Container, Functors, and Lists

In mathematics, a functor is a map between categories. [2] While containers are defined intuitively as wrappers that encapsulate variables which may have a null type. In JavaScript containers don't have a data type like in strongly typed languages like Java or C/C++; instead the containers hold any data, giving us the advantage of manipulating them in a genuinely generic way.
On of the most common examples of functors working on containers is Array.prototype.map, like in the following case [3]

var map = Array.prototype.map;
var a = map.call('Hello World', function(x) { 
  return x.charCodeAt(0); 
});
// a now equals [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

Where we're using map to get an array of bytes in the ASCII encoding, from the character values.

Declarative vs. Imperative

Functional programming is a declarative paradigm, meaning that the program logic works without explicitly describing the flow control. [1:1]

Imperative

In computer science, imperative programming is a programming paradigm that uses statements that change a program's state. In much the same way that the imperative mood in natural languages expresses commands, an imperative program consists of commands for the computer to perform. Imperative programming focuses on describing how a program operates. [4]

Take the following example,

const intArray = [1, 2, 3, 4, 5];
let sum = 0;

for(let i = 0; i < intArray.length; i++) {
  sum += sum[i];
}
console.log(sum); // sum = 15

To calculate the sum of all elements in intArray, we're explicitly adding the value of each element, from index 0 to index 4, defining a control flow.

Declarative

Declarative programming is a programming paradigm—a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow.[5]

Going back to the imperative example, we can calculate a sum of intArray without defining a control flow,

const intArray = [1, 2, 3, 4, 5];
let sum = 0;

sum = intArray.reduce( (acc, cur) => acc+cur, 0);
console.log(sum);

Instead of using a series of steps, we use the function reduce that abstracts the process and only takes the operation executed for each element in the container.

Thank you so much for reading this article. Take care, and we'll continue reviewing the next Programming paradigm in JavaScript: Object Oriented Programming.


  1. Master the JavaScript Interview: What is Functional Programming? ↩︎ ↩︎

  2. Functor ↩︎

  3. Array.prototype.map() ↩︎

  4. Imperative programming ↩︎

  5. Declarative programming ↩︎

Author image
Costa Rica
Passionate Software Developer with full-stack development experience.