Introduction to Functional Programming

Functional Programming (FP) is a programming paradigm that includes specific techniques. There are both purely functional programming languages like Haskell, Clojure, and Scala, as well as programming languages that support functional programming techniques, such as JavaScript, Python, and Ruby.

While the concept of functional programming dates back to the 1930s with the birth of lambda calculus, it has gained significant momentum recently. This is the perfect time to learn about functional programming, especially through JavaScript examples.

First Class Functions

In functional programming languages, functions are considered first-class citizens. This means that functions can be assigned to variables, used as arguments to other functions, and returned by functions.

For example, functions can be assigned to variables:

const f = (m) => console.log(m);
f('Test');

Functions can also be added to objects:

const obj = {
  f(m) {
    console.log(m);
  }
};
obj.f('Test');

And they can be added to arrays:

const a = [
  m => console.log(m)
];
a[0]('Test');

Higher Order Functions

Functions that accept functions as arguments or return functions are called Higher Order Functions. Examples in the JavaScript standard library include Array.map(), Array.filter(), and Array.reduce(), which we’ll explore later.

Declarative Programming

Declarative programming is an approach where you tell the machine what you need to do, and let it figure out the details. It is the opposite of imperative programming, where you explicitly tell the machine the steps it needs to take.

A declarative approach is more abstract and less focused on telling the machine each step of processing. In JavaScript, you can take a declarative programming approach by using functional programming constructs like map, reduce, and filter instead of loops.

Immutability

In functional programming, data is immutable, meaning it never changes. To update a variable or data structure, you create a new variable or data structure instead of modifying the original. This approach enforces immutability and helps avoid side effects.

In JavaScript, you can enforce immutability by using const to declare variables. You can also use methods like Object.assign() to create new objects based on existing ones. To add or remove items from an array, you can use methods like concat() and filter() instead of mutating the original array.

Purity

A pure function is a function that has the following characteristics:

  • It does not change its input parameters.
  • The return value is solely dependent on the input parameters.
  • It does not modify anything outside of its scope.

Pure functions are predictable, easier to reason about, and can be safely reused and tested.

Data Transformations

Since immutability is a fundamental concept in functional programming, data changes are achieved by creating copies of data. Functions like map and reduce are commonly used for data transformations.

The Array.map() method creates a new array by applying a function to each item of the original array:

const a = [1, 2, 3];
const b = a.map((v, k) => v * k);
// b = [0, 2, 6]

The Array.reduce() method allows you to transform an array into any other value. You pass a function that processes the result and a starting point:

const a = [1, 2, 3];
const sum = a.reduce((partial, v) => partial + v, 0);
// sum = 6

const o = a.reduce((obj, k) => { obj[k] = k; return obj }, {});
// o = {1: 1, 2: 2, 3: 3}

Recursion

Recursion is a key topic in functional programming. It occurs when a function calls itself. Recursive functions are commonly used for solving problems that can be naturally expressed in terms of smaller instances of the same problem.

A classic example of recursion is the Fibonacci sequence calculation:

var f = (n) => n <= 1 ? 1 : f(n-1) + f(n-2);

Composition

Composition is the act of combining simpler functions to create a higher-order function. It allows functions to be chained or passed as arguments to other functions.

In plain JavaScript, function composition can be achieved by chaining functions or passing a function execution into another function. Libraries like lodash provide utility functions, such as compose, to facilitate composition.

For example, with lodash/fp’s compose function, you can execute a list of functions, where each function inherits its argument from the preceding function’s return value:

import { compose } from 'lodash/fp';

const slugify = compose(
  encodeURIComponent,
  join('-'),
  map(toLowerCase),
  split(' ')
);

slugify('Hello World'); // hello-world