Functional Programming Techniques
Functional programming patterns and techniques
Challenge
Section titled “Challenge”Apply functional programming principles for cleaner, more predictable code.
Explanation
Section titled “Explanation”Functional programming emphasizes immutability, pure functions, and composition.
Pure Functions
Section titled “Pure Functions”// Pure: Same input always produces same output, no side effectsfunction add(a, b) { return a + b;}
// Impure: Depends on external state, has side effectslet total = 0;function addToTotal(amount) { total += amount; // Side effect return total;}
// Impure: Modifies inputfunction addItem(array, item) { array.push(item); // Mutates original return array;}
// Pure: Doesn't modify inputfunction addItem(array, item) { return [...array, item]; // Creates new array}Function Composition
Section titled “Function Composition”// Combine functionsconst compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x);
// Usageconst add5 = x => x + 5;const multiply2 = x => x * 2;const square = x => x * x;
const composed = compose(square, multiply2, add5);console.log(composed(3)); // ((3 + 5) * 2)² = 256
const piped = pipe(add5, multiply2, square);console.log(piped(3)); // ((3 + 5) * 2)² = 256Higher-Order Functions
Section titled “Higher-Order Functions”// Functions that take or return functionsfunction withLogging(fn) { return function(...args) { console.log('Calling', fn.name, 'with', args); const result = fn(...args); console.log('Result:', result); return result; };}
const add = (a, b) => a + b;const loggedAdd = withLogging(add);loggedAdd(2, 3);// Calling add with [2, 3]// Result: 5
// Function returning functionfunction multiply(factor) { return (number) => number * factor;}
const double = multiply(2);const triple = multiply(3);console.log(double(5)); // 10console.log(triple(5)); // 15Partial Application and Bind
Section titled “Partial Application and Bind”// Partial application: Preset some argumentsfunction sum(a, b, c) { return a + b + c;}
function partial(fn, ...args) { return (...moreArgs) => fn(...args, ...moreArgs);}
const add5 = partial(sum, 5);console.log(add5(2, 3)); // 10
// Built-in bindconst boundAdd = sum.bind(null, 5);console.log(boundAdd(2, 3)); // 10Immutability
Section titled “Immutability”// ❌ Mutablelet user = { name: 'John', age: 30 };user.age = 31;
// ✅ Immutableconst user = { name: 'John', age: 30 };const updatedUser = { ...user, age: 31 };
// ✅ Arraysconst numbers = [1, 2, 3];const newNumbers = [...numbers, 4];const mapped = numbers.map(n => n * 2);
// Prevent mutationsObject.freeze(user);// user.age = 31; // Silently fails (or throws in strict mode)Memoization
Section titled “Memoization”// Cache function resultsfunction memoize(fn) { const cache = {};
return function(...args) { const key = JSON.stringify(args);
if (key in cache) { return cache[key]; }
const result = fn(...args); cache[key] = result; return result; };}
function expensive(n) { console.log('Computing...'); return n * n;}
const memoized = memoize(expensive);console.log(memoized(5)); // Computing... 25console.log(memoized(5)); // 25 (cached)Recursion
Section titled “Recursion”// Tail recursionfunction factorial(n, acc = 1) { if (n <= 1) return acc; return factorial(n - 1, n * acc); // Tail call}
// Reduce with recursionfunction reduce(fn, acc, [head, ...tail]) { if (!head) return acc; return reduce(fn, fn(acc, head), tail);}
const sum = reduce((a, b) => a + b, 0, [1, 2, 3, 4]);console.log(sum); // 10Lens Pattern
Section titled “Lens Pattern”// Focus on nested object propertiesconst lensProp = (prop) => ({ get: (obj) => obj[prop], set: (obj, value) => ({ ...obj, [prop]: value })});
const addressLens = lensProp('address');const user = { name: 'John', address: '123 St' };
const view = addressLens.get(user); // '123 St'const updated = addressLens.set(user, '456 Ave');// { name: 'John', address: '456 Ave' }