tip minersTip Miners

Functional Programming Pillars

Written by Miguel

Derived from the fundamental rules of immutability and No side effects, functional programming bases its foundation on the following four pillars: Pure Functions, First Class Functions, Composition, and Recursion.

Now, before we dive into this pillars I want to give you peace of mind if you are already getting bored , this are building blocks concepts that you need to keep in mind while constructing more elaborated code pieces 👌🏼 , what you are about to read are just simple mental models of this pillars that will allow you to grasp the idea around them.

Pure Functions - A Neat Backery

Pure Functions are like a bakery that produces the same cake base on the same ingredients

In FP, pure functions are like a bakery that takes specific ingredients, follows a recipe, and produces a cake. The same ingredients and recipe will always yield the same cake. No surprises. Pure functions don't depend on external factors, and they don't alter anything outside—they just bake the cake.

// Impure function
let globalVariable = 10;
function impureAdd(x: number): number {
  return x + globalVariable; // Depends on external state
}

// Pure function
function pureAdd(x: number, y: number): number {
  return x + y; // No reliance on external state
}

Backend Example:

Imagine you're building an e-commerce platform. You have a function that calculates the total price of a shopping cart. A pure function for this scenario might look something like this in TypeScript:

function calculateTotalPrice(items: Item[]): number {
  return items.reduce((total, item) => total + item.price, 0);
}

This function takes an array of items and returns the total price. It doesn't modify the items array or any other external state, making it pure and predictable.

Frontend Example:

Now, let's consider a front-end scenario. You're developing a user interface where users can apply filters to search for products. You have a function that filters products based on certain criteria. Here's how you might implement it using TypeScript:

function filterProducts(products: Product[], criteria: string): Product[] {
  return products.filter((product) => product.category === criteria);
}

This function takes a list of products and a filter criteria, returning only the products that match the criteria. It doesn't modify the original list of products or any other external state.

Benefits of Pure Functions:

Pure Functions in React:

In React, pure functions play a crucial role in optimizing performance. Components that are pure functions of their props can be memoized, preventing unnecessary re-renders and improving the overall performance of your application.

Pure Function Example:

const Greeting: React.FC<{ name: string }> = ({ name }) => {
  return <div>Hello, {name}!</div>;
};

The Greeting component takes a name prop and returns a greeting message. Since it only depends on its props, it's a pure function and can be memoized for better performance.

First-Class Functions: The Delivery Service

In Functional Programming functions act like packages a couriers in a delevery service

In FP, functions are first-class citizens, meaning they can be passed around like any other value. It's akin to a delivery service that can take a package (function) and deliver it to different locations in your code. You can send functions as arguments, return them from other functions, or store them in variables.

// First-class function
const double = (x: number): number => x * 2;

// Higher-order function
const applyOperation = (
  operation: (x: number) => number,
  value: number
): number => operation(value);

// Using the delivery service
const result = applyOperation(double, 5); // Result: 10

Backend Example:

Imagine you're building a backend API for a social media platform. You have a function that validates user authentication tokens. In TypeScript, it might look like this:

function validateAuthToken(token: string): boolean {
  // Logic to validate the authentication token
  return true; // Return true if the token is valid, false otherwise
}

const authenticateUser = (token: string, callback: Function) => {
  if (validateAuthToken(token)) {
    callback();
  } else {
    console.log("Invalid token. Please log in again.");
  }
};

// Example usage
authenticateUser("user_auth_token", () => {
  console.log("User authenticated successfully!");
});

Here, the authenticateUser function takes an authentication token and a callback function. If the token is valid, it invokes the callback function. This demonstrates how functions can be passed around as arguments.

Frontend Example:

Let's switch gears to a front-end scenario. You're developing a web application with a feature that allows users to sort a list of products based on different criteria. You have an array of products and a function to perform the sorting. Here's how you might implement it:

const products = [
  { name: "Laptop", price: 999 },
  { name: "Smartphone", price: 699 },
  { name: "Tablet", price: 399 },
];

function sortByPrice(a: Product, b: Product): number {
  return a.price - b.price;
}

// Sorting products by price
const sortedProducts = products.sort(sortByPrice);
console.log(sortedProducts);

In this example, the sortByPrice function is passed as an argument to the sort method of the products array. This demonstrates how functions can be stored in variables and used as values.

Benefits of First-Class Functions:

Function Composition: LEGO Blocks Assembling

Function Composition is like assembling LEGO blocks

Function composition is like assembling LEGO blocks. Each function is a LEGO piece, and you combine them to build more complex structures. You start with simple functions and compose them to create intricate behaviors. It's modular, reusable, and results in a well-structured application.

// Function composition
const add = (x: number, y: number): number => x + y;
const multiplyBy2 = (x: number): number => x * 2;

// Composing functions
const addAndMultiply = (x: number, y: number): number => multiplyBy2(add(x, y));

// Result: (3 + 2) * 2 = 10
const finalResult = addAndMultiply(3, 2);

Recursion: Opening Russian Dolls

Recursion is like opening nested Russian dolls

Recursion is like opening nested Russian dolls. Each doll contains a smaller one until you reach the smallest. In FP, a function calls itself with smaller inputs, solving a problem by breaking it down into simpler instances of the same problem.

// Recursive function to calculate factorial
const factorial = (n: number): number => {
  if (n === 0 || n === 1) {
    return 1;
  }
  return n * factorial(n - 1);
};

// Result: 5! = 5 * 4 * 3 * 2 * 1 = 120
const result = factorial(5);

Note on recursion : out in the wild , is not really highly used since it can have performance issues and when is used wrongly ( poor stop conditions , for example ) it can be self destructive for your programs. In consequence , just learn the concept and use it with caution.

Tips to start using this concepts

Just one : Remember that functions can be passed as parameters and returned from other functions.

Next - Higher Order Functions