Skip to main content

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! ⚡