λ

Functional Programming in JavaScript

JavaScriptAdvanced

Pure functions, immutability, and composition.

Code Example

JavaScript
// 1. Pure functions - no side effects
function pureAdd(a, b) {
  return a + b;
}

// Impure - modifies external state
let total = 0;
function impureAdd(n) {
  total += n; // Side effect
  return total;
}

// 2. Immutability
const original = [1, 2, 3];

// BAD: Mutates original
// original.push(4);

// GOOD: Creates new array
const newArray = [...original, 4];
console.log(original); // [1, 2, 3]
console.log(newArray); // [1, 2, 3, 4]

// Immutable object updates
const user = { name: 'Alice', age: 30 };
const updatedUser = { ...user, age: 31 };
console.log(user); // { name: 'Alice', age: 30 }
console.log(updatedUser); // { name: 'Alice', age: 31 }

// 3. Higher-order functions
function map(array, fn) {
  const result = [];
  for (const item of array) {
    result.push(fn(item));
  }
  return result;
}

const doubled = map([1, 2, 3], x => x * 2);
console.log(doubled); // [2, 4, 6]

// 4. Function composition
const compose = (...fns) => (x) => 
  fns.reduceRight((acc, fn) => fn(acc), x);

const add10 = x => x + 10;
const multiply2 = x => x * 2;
const subtract5 = x => x - 5;

const transform = compose(subtract5, multiply2, add10);
console.log(transform(5)); // (5 + 10) * 2 - 5 = 25

// 5. Currying
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    }
    return (...moreArgs) => curried(...args, ...moreArgs);
  };
}

const add = curry((a, b, c) => a + b + c);
console.log(add(1)(2)(3)); // 6
console.log(add(1, 2)(3)); // 6
console.log(add(1)(2, 3)); // 6

// 6. Point-free style
const numbers = [1, 2, 3, 4, 5];

// With points
const sum1 = numbers.reduce((acc, n) => acc + n, 0);

// Point-free
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
console.log(sum(numbers)); // 15

// 7. Memoization
function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

const factorial = memoize((n) => {
  if (n <= 1) return 1;
  return n * factorial(n - 1);
});

console.log(factorial(5)); // 120
console.log(factorial(5)); // Cached

// 8. Functors and Monads (conceptual)
class Maybe {
  constructor(value) {
    this.value = value;
  }
  
  map(fn) {
    return this.value === null || this.value === undefined
      ? new Maybe(null)
      : new Maybe(fn(this.value));
  }
  
  chain(fn) {
    return this.map(fn).value;
  }
}

const maybeValue = new Maybe(5);
const result = maybeValue
  .map(x => x * 2)
  .map(x => x + 1);
console.log(result.value); // 11

// 9. Immutable data structures
class ImmutableList {
  constructor(items = []) {
    this.items = Object.freeze([...items]);
  }
  
  add(item) {
    return new ImmutableList([...this.items, item]);
  }
  
  remove(index) {
    return new ImmutableList(
      this.items.filter((_, i) => i !== index)
    );
  }
  
  get(index) {
    return this.items[index];
  }
}

const list1 = new ImmutableList([1, 2, 3]);
const list2 = list1.add(4);
console.log(list1.items); // [1, 2, 3]
console.log(list2.items); // [1, 2, 3, 4]

// 10. Lens (functional getters/setters)
function lens(getter, setter) {
  return { getter, setter };
}

const nameLens = lens(
  obj => obj.name,
  (obj, value) => ({ ...obj, name: value })
);

const person = { name: 'Alice', age: 30 };
const updated = nameLens.setter(person, 'Bob');
console.log(updated); // { name: 'Bob', age: 30 }

Explanation

Pure functions have no side effects. Immutable data never changes. Compose small functions. Curry for partial application. Memoize expensive operations. Use libraries like Immutable.js for complex structures.

Related Tutorials