Chapter 10: Functional Programming
Chapter 10 of 15
Chapter 10: Functional Programming
10.1 Pure Functions
Pure functions are functions that always return the same output for the same input and have no side effects. They are predictable and easier to test.
Characteristics of Pure Functions:
- No side effects (don't modify external state)
- Deterministic (same input = same output)
- Referentially transparent (can be replaced with its value)
// Pure function
function add(a, b) {
return a + b;
}
// Impure function (has side effect)
let counter = 0;
function impureAdd(a, b) {
counter++; // Side effect
return a + b;
}
// Impure function (modifies input)
function impureDouble(arr) {
for (let i = 0; i < arr.length; i++) {
arr[i] *= 2; // Mutates input
}
return arr;
}
// Pure version
function pureDouble(arr) {
return arr.map(n => n * 2); // Returns new array
}
Benefits of Pure Functions:
- Easier to test
- Easier to reason about
- Can be memoized
- Can be parallelized
- No hidden dependencies
10.2 Immutability
Immutability means data cannot be changed after creation. Instead of modifying data, you create new data structures.
// Mutable approach
const user = { name: 'John', age: 30 };
user.age = 31; // Mutates original object
// Immutable approach
const updatedUser = { ...user, age: 31 }; // New object
// Arrays - mutable
const arr = [1, 2, 3];
arr.push(4); // Mutates array
// Arrays - immutable
const newArr = [...arr, 4]; // New array
const newArr2 = arr.concat(4); // Also creates new array
Immutability with Nested Objects:
const user = {
name: 'John',
address: {
city: 'NYC',
zip: '10001'
}
};
// Shallow copy (address is still mutable)
const shallowCopy = { ...user };
shallowCopy.address.city = 'LA'; // Also changes original
// Deep copy
const deepCopy = JSON.parse(JSON.stringify(user));
// Or use libraries like Immutable.js or Immer
// Using Immer (popular library)
import produce from 'immer';
const updatedUser = produce(user, draft => {
draft.address.city = 'LA';
});
10.3 Higher-Order Functions in FP
// Function composition
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);
const addOne = x => x + 1;
const multiplyByTwo = x => x * 2;
const square = x => x * x;
const transform = pipe(addOne, multiplyByTwo, square);
console.log(transform(3)); // ((3 + 1) * 2)² = 64
10.4 Recursion
// Recursive factorial
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// Tail-recursive version (optimized)
function factorialTail(n, acc = 1) {
if (n <= 1) return acc;
return factorialTail(n - 1, n * acc);
}
// Recursive array operations
function deepMap(arr, fn) {
return arr.map(item =>
Array.isArray(item) ? deepMap(item, fn) : fn(item)
);
}