Skip to content
Plurafy

Functional Programming Techniques

Functional programming patterns and techniques

Apply functional programming principles for cleaner, more predictable code.

Functional programming emphasizes immutability, pure functions, and composition.

// Pure: Same input always produces same output, no side effects
function add(a, b) {
return a + b;
}
// Impure: Depends on external state, has side effects
let total = 0;
function addToTotal(amount) {
total += amount; // Side effect
return total;
}
// Impure: Modifies input
function addItem(array, item) {
array.push(item); // Mutates original
return array;
}
// Pure: Doesn't modify input
function addItem(array, item) {
return [...array, item]; // Creates new array
}
// Combine functions
const compose = (...fns) => x =>
fns.reduceRight((acc, fn) => fn(acc), x);
const pipe = (...fns) => x =>
fns.reduce((acc, fn) => fn(acc), x);
// Usage
const 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)² = 256
// Functions that take or return functions
function 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 function
function multiply(factor) {
return (number) => number * factor;
}
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// Partial application: Preset some arguments
function 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 bind
const boundAdd = sum.bind(null, 5);
console.log(boundAdd(2, 3)); // 10
// ❌ Mutable
let user = { name: 'John', age: 30 };
user.age = 31;
// ✅ Immutable
const user = { name: 'John', age: 30 };
const updatedUser = { ...user, age: 31 };
// ✅ Arrays
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4];
const mapped = numbers.map(n => n * 2);
// Prevent mutations
Object.freeze(user);
// user.age = 31; // Silently fails (or throws in strict mode)
// Cache function results
function 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... 25
console.log(memoized(5)); // 25 (cached)
// Tail recursion
function factorial(n, acc = 1) {
if (n <= 1) return acc;
return factorial(n - 1, n * acc); // Tail call
}
// Reduce with recursion
function 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); // 10
// Focus on nested object properties
const 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' }