Functional Programming
Functional Programming (FP) is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data.
Core Concepts
Pure Functions
A pure function always produces the same output for the same input and has no side effects.
// Pure function
const add = (a, b) => a + b;
add(2, 3); // Always returns 5
// Impure function (has side effects)
let total = 0;
const addToTotal = (value) => {
total += value; // Modifies external state
return total;
};
Immutability
Instead of changing data, create new data:
// Immutable approach
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4]; // Creates new array
console.log(numbers); // [1, 2, 3]
console.log(newNumbers); // [1, 2, 3, 4]
// Mutable approach (avoid)
numbers.push(4); // Modifies original array
Higher-Order Functions
Functions that take functions as arguments or return functions:
// Map: Transform each element
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
// [2, 4, 6, 8, 10]
// Filter: Select elements
const evens = numbers.filter(x => x % 2 === 0);
// [2, 4]
// Reduce: Aggregate elements
const sum = numbers.reduce((acc, x) => acc + x, 0);
// 15
// Function composition
const compose = (f, g) => x => f(g(x));
const double = x => x * 2;
const increment = x => x + 1;
const doubleThenIncrement = compose(increment, double);
console.log(doubleThenIncrement(3)); // 7
Examples in Different Languages
Haskell
-- Pure functional language
fibonacci :: Int -> Int
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n-1) + fibonacci (n-2)
-- List processing
doubleList :: [Int] -> [Int]
doubleList = map (*2)
-- Higher-order functions
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
JavaScript
// Functional style in JavaScript
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 35 }
];
const getAdultNames = users
.filter(user => user.age >= 18)
.map(user => user.name)
.sort();
Python
# Functional programming in Python
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# Map, filter, reduce
doubled = list(map(lambda x: x * 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, numbers))
sum_all = reduce(lambda acc, x: acc + x, numbers, 0)
# List comprehensions (Pythonic functional style)
squares = [x**2 for x in numbers]
even_squares = [x**2 for x in numbers if x % 2 == 0]
Recursion
FP often uses recursion instead of loops:
// Factorial using recursion
const factorial = n => {
if (n <= 1) return 1;
return n * factorial(n - 1);
};
// Sum array using recursion
const sumArray = arr => {
if (arr.length === 0) return 0;
const [head, ...tail] = arr;
return head + sumArray(tail);
};
// Fibonacci
const fib = n => {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
};
Function Composition
// Compose functions
const compose = (...fns) => x =>
fns.reduceRight((acc, fn) => fn(acc), x);
// Pipe functions (left to right)
const pipe = (...fns) => x =>
fns.reduce((acc, fn) => fn(acc), x);
// Example
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const transform = pipe(
addOne, // 5 + 1 = 6
double, // 6 * 2 = 12
square // 12 * 12 = 144
);
console.log(transform(5)); // 144
Advantages
✅ Predictable
- Pure functions always return same result
- Easy to test
- No hidden dependencies
✅ Concurrent-Friendly
- Immutable data prevents race conditions
- Easy to parallelize
- No shared state issues
✅ Composable
- Build complex functions from simple ones
- Reusable building blocks
- Mathematical elegance
✅ Easier Debugging
- No side effects to track
- Deterministic behavior
- Function inputs/outputs tell the whole story
Disadvantages
❌ Learning Curve
- Different way of thinking
- Mathematical concepts
- Recursion can be tricky
❌ Performance
- Creating new data structures overhead
- Stack overflow with deep recursion
- Not always most efficient
❌ Verbosity
- Can require more code
- Multiple transformations
- Not always intuitive
Common Patterns
Currying
// Convert multi-argument function to single-argument functions
const curry = fn => {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...moreArgs) => curried(...args, ...moreArgs);
};
};
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
Partial Application
const partial = (fn, ...fixedArgs) => {
return (...remainingArgs) => {
return fn(...fixedArgs, ...remainingArgs);
};
};
const multiply = (a, b, c) => a * b * c;
const double = partial(multiply, 2);
console.log(double(3, 4)); // 24
When to Use Functional Programming
✅ Perfect For:
- Data Transformation Pipelines: Map/filter/reduce
- Concurrent Systems: No shared state
- Mathematical Computations: Natural fit
- Testing: Pure functions easy to test
- Reactive Programming: Event streams
❌ Less Suitable For:
- I/O Heavy Applications: Side effects necessary
- Performance-Critical: Overhead can matter
- UI with Complex State: Stateful easier sometimes
- Systems Programming: Low-level control needed
Conclusion
Functional programming offers a powerful alternative to imperative programming, with benefits in testing, concurrency, and code clarity. Modern languages increasingly support FP features! ⚡