Functional programming (FP) is a style that models programs as transformations of data. Instead of mutating state step by step, we build small functions and combine them to express intent.
Python is multi-paradigm, so we can use FP ideas without writing purely
functional code all the time. In practice, this gives us readable pipelines,
testable logic, and fewer
Why Use FP Concepts
FP principles are useful because they improve predictability.
- Pure functions are easier to test.
- Immutable data reduces accidental bugs.
- Composition helps build reusable logic.
- Stateless functions are easier to parallelize.
Core Concepts
Pure Functions
A pure function returns the same output for the same input and does not change external state.
def celsius_to_fahrenheit(celsius: float) -> float:
return celsius * 9 / 5 + 32
The result depends only on celsius, and the function has no observable side
effects. Avoiding global writes alone does not guarantee determinism: reading a
clock or mutable global state can also change a result.
Immutability
Immutability means creating new values instead of mutating existing ones.
def add_item(items: tuple[str, ...], item: str) -> tuple[str, ...]:
return items + (item,)
Using immutable structures makes data flow easier to reason about.
Higher-Order Functions
Higher-order functions receive functions as arguments or return functions.
def apply_twice(fn, value):
return fn(fn(value))
print(apply_twice(lambda x: x + 3, 10)) # 16
This pattern lets us abstract behavior, not just data.
Referential Transparency
If an expression can be replaced by its value without changing behavior, it is referentially transparent. This simplifies debugging and refactoring.
For example, replacing double(5) with 10 is always safe if double is pure.
Function Composition
Composition means combining small functions into larger ones.
def compose(f, g):
return lambda x: f(g(x))
strip_and_lower = compose(str.lower, str.strip)
print(strip_and_lower(" Hello ")) # hello
Useful FP Tools in Python
Python includes many features that support FP style.
map, filter, and reduce
from functools import reduce
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
evens = list(filter(lambda x: x % 2 == 0, doubled))
total = reduce(lambda acc, x: acc + x, evens, 0)
print(doubled) # [2, 4, 6, 8, 10]
print(evens) # [2, 4, 6, 8, 10]
print(total) # 30
map and filter, but both
approaches are valid.
partial for Function Specialization
from functools import partial
def power(base: int, exponent: int) -> int:
return base ** exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25
print(cube(3)) # 27
This is partial application: fixing some arguments to obtain a new callable.
Currying instead transforms a function of several arguments into a sequence of
functions taking one argument each.
See functools.partialdocs.python.orgPython documentation for functools.partial, which returns a new callable with some arguments already fixed.
Practical Pipeline Example
Suppose we receive numeric strings and blank lines and want the sum of positive
amounts. This example skips blanks; malformed nonblank strings raise
ValueError.
from typing import Iterable
def parse_amounts(lines: Iterable[str]) -> list[float]:
return [float(line) for line in lines if line.strip()]
def only_positive(values: Iterable[float]) -> list[float]:
return [v for v in values if v > 0]
def total(values: Iterable[float]) -> float:
return sum(values)
raw = ["100", "-40", "", "18.5", "-2", "7.5"]
result = total(only_positive(parse_amounts(raw)))
print(result) # 126.0
Each step is small, focused, and easy to test in isolation.
Recursion and Python
Recursion is common in FP, but Python does not optimize
Use recursion when it improves clarity, but keep Python’s
Common Pitfalls
- Overusing
lambdasAnonymous single-expression functions written with the lambda keyword. and making code harder to read. - Forcing
reducewhensumor a loop is clearer. - Mixing pure and impure logic in the same function.
- Ignoring readability in the name of style purity.
FP is most valuable when it improves maintainability, not when it is applied as a strict rule.
Final Thoughts
Functional programming in Python is about disciplined data transformation. You can gradually adopt it by writing pure helper functions, limiting mutation, and composing operations into clear pipelines.
Used well, FP makes code easier to test, reason about, and evolve.
References
-
HaskellWiki: Functional programmingwiki.haskell.orgHaskellWiki's introduction to functional programming and its core ideas, such as purity and higher-order functions. -
Haskell Languagehaskell.orgHome page of Haskell, a purely functional programming language.