Functional Programming  

Thinking in Functions

Pure Functions

Key Properties

  • Same Input ➡️ Same Output (Deterministic)
  • Has no side effects

Why purity matters

  • Predictability
  • Testability
  • Referential transparency (replace function with return value)

Pure or Poisoned?

function add(a, b) {
  return a + b;
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

Pure or Poisoned?

function add(a, b) {
  return a + b;
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

PURE

Pure or Poisoned?

let taxRate = 0.14;

function calculatePrice(price) {
  return price + price * taxRate;
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

Pure or Poisoned?

let taxRate = 0.14;

function calculatePrice(price) {
  return price + price * taxRate;
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

IMPURE

Why?

  • Depends on an external variable.

Pure or Poisoned?

function addItem(cart, item) {
  cart.push(item);
  return cart;
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

Pure or Poisoned?

function addItem(cart, item) {
  cart.push(item);
  return cart;
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

IMPURE

Why?

  • Mutates the input array

  • The same input reference can give different results

  • Breaks immutability

Pure or Poisoned?

function formatUser(user) {
  return {
    ...user,
    lastLogin: new Date().toISOString()
  };
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

Pure or Poisoned?

function formatUser(user) {
  return {
    ...user,
    lastLogin: new Date().toISOString()
  };
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

IMPURE

Why?

  • Uses Date() (time dependency)

  • Same input → different output every call

  • Breaks determinism

Pure or Poisoned?

function createCounter(start) {
  let count = start;

  return () => {
    count++;
    return count;
  };
}

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

Pure or Poisoned?

function createCounter(start) {
  let count = start;

  return () => {
    count++;
    return count;
  };
}


const counter = createCounter(0);
counter(); // 1
counter(); // 2 ❌

Rules

  • Same input ➡️ Same Output
  • No side effects
  • referentially transparent

IMPURE

Why?

  • Returned function produces different output on each call

  • Not referentially transparent

First Class Function

function createCounter(start) {
  let count = start;

  return () => {
    count++;
    return count;
  };
}


const counter = createCounter(0);
counter(); // 1
counter(); // 2 

A language has first-class functions when functions are treated like values. 

First Class Function

function createCounter(start) {
  let count = start;

  return () => {
    count++;
    return count;
  };
}


const counter = createCounter(0);
counter(); // 1
counter(); // 2 
  • store them in variables
  • pass them to other functions
  • return them from functions
  • put them in arrays or objects

A language has first-class functions when functions are treated like values. 

First Class Function

A language has first-class functions when functions are treated like values. 

function createCounter(start) {
  let count = start;

  return () => {
    count++;
    return count;
  };
}


const counter = createCounter(0);
counter(); // 1
counter(); // 2 

Long story short first-class function

is a Language Feature

  • store them in variables
  • pass them to other functions
  • return them from functions
  • put them in arrays or objects

Higher-order functions (HOFs)

A higher-order function is any function that does one of these:

function createCounter(start, triple) {
  let count = start;

  return () => {
    count++;
    return triple(count);
  };
}


const counter = createCounter(0);
counter(); // 3
counter(); // 6
  • takes another function as an argument
  • returns a function

Higher-order functions (HOFs)

A higher-order function is any function that does one of these:

function createCounter(start, triple) {
  let count = start;

  return () => {
    count++;
    return triple(count);
  };
}


const counter = createCounter(0);
counter(); // 3
counter(); // 6

HOFs exist because functions are first-class

  • takes another function as an argument
  • returns a function
  • First-class functions → a language feature
  • Higher-order functions → a pattern that becomes possible because of that feature

Imperative

Declarative

VS

function resolveCartTotal(cartItems) {
  let total = 0;

  for (let i = 0; i < cartItems.length; i++) {
    const item = cartItems[i];

    if (!item.isGift) {
      total += item.price * item.quantity;
    }
  }

  return total;
}
function resolveCartTotal(cartItems) {
  return cartItems
    .filter((item) => !item.isGift)
    .map((item) => item.price * item.quantity)
    .reduce((sum, price) => sum + price, 0);
}

How to do it, step by step

What I want

Recursion

Each recursive call returns a new value instead of modifying variables

keeping functions pure and referentially transparent.

const imperativeFactorial = (n) => {
    let product = 1;
    while (n > 0) {
        product *= n;
        n--;
    }
    return product;
};

recursive

const recursiveFactorial = (n) => {
    if (n === 0) return 1;
    return n * recursiveFactorial(n - 1);
};

Recursion

Each recursive call returns a new value instead of modifying variables

keeping functions pure and referentially transparent.

const imperativeFactorial = (n) => {
    let product = 1;
    while (n > 0) {
        product *= n;
        n--;
    }
    return product;
};

recursive

const recursiveFactorial = (n) => {
    if (n === 0) return 1;
    return n * recursiveFactorial(n - 1);
};

‼️Warning

Too much recursion could lead to a stack overflow

Use Tail Call Optimization (TCO)

Solution

Currying

function fetchData(baseUrl, endpoint, options = {}) {
  return fetch(`${baseUrl}${endpoint}`, {
    method: "GET",
    headers: {
      Authorization: "Bearer TOKEN",
      "Content-Type": "application/json",
    },
    ...options,
  }).then((res) => res.json());
}

Currying transforms a function that takes multiple arguments into a chain of functions that take one argument at a time.

Currying

const createFetchClient = (baseUrl) => (defaultOptions) => (endpoint) =>
  fetch(`${baseUrl}${endpoint}`, defaultOptions).then((res) => res.json());

const apiClient = createFetchClient("https://api.example.com")({
  headers: {
    Authorization: "Bearer TOKEN",
    "Content-Type": "application/json",
  },
});

apiClient("/users");
apiClient("/products");
apiClient("/orders");

Currying transforms a function that takes multiple arguments into a chain of functions that take one argument at a time.

  • Currying is powerful when it improves reuse and composition.
  • Currying is harmful when it hurts readability and clarity.

Composition

Functional programming focuses on small and pure functions.

Using composition, we can use small functions to construct complex ones

const trim = str => str.trim();
const toLower = str => str.toLowerCase();
const removeSpaces = str => str.replace(/\s+/g, '');

const normalizeInput = pipe(
  trim,
  toLower,
  removeSpaces
);

normalizeInput("  He LLo  "); // "hello"

Compose vs Pipe

const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((value, fn) => fn(value), x);

const double = (x) => x * 2;
const addOne = (x) => x + 1;

const process = compose(addOne, double);

process(3); // 7

Compose and pipe are HOF.

Used for building complex functions by connecting smaller ones

Compose

Pipe

const pipe =
  (...fns) =>
  (x) =>
    fns.reduce((value, fn) => fn(value), x);

const double = (x) => x * 2;
const addOne = (x) => x + 1;

const process = pipe(double, addOne);

process(3); // 7

VS

Left ⬅️ Right

Left ➡️ Right

Think Functions

  • treat data as read-only
  • isolate and minimize side effects.
  • declarative, FP cares about values, not steps
  •  Break Problems Into Tiny Functions
  • Think in Pipelines

Don't forget, use FP ideas where they fit

DON'T FORCE

Functional Programming

By Mohamed Dewidar

Functional Programming

  • 11