Overview
Functional programming is a programming paradigm that models computation primarily as the evaluation of mathematical functions and avoids changing state or mutable data. It is often described as a declarative approach: programs specify what to compute rather than how to step through changes. Many practitioners favor functional techniques because they tend to produce code that is easier to reason about, test, and parallelize.
Core concepts
Several recurring ideas define the style:
- Pure functions: Functions that always return the same result for the same inputs and produce no side effects.
- Immutability: Data structures are not altered in place; new versions are created when changes are needed.
- First-class and higher-order functions: Functions are values that can be passed, returned, and composed. See functions as values.
- Referential transparency: Expressions can be replaced by their values without changing program behavior; a property many functional programmers aim to preserve. See referential transparency.
- Lazy evaluation: Some languages delay computation until results are required, enabling infinite structures and efficient pipelines.
History and lineage
Theoretical roots lie in the lambda calculus, a formal system for functions and substitution. That theory influenced early and later languages, from Lisp and ML to modern pure functional languages and libraries. Functional ideas have been adopted incrementally in mainstream languages and libraries, blending with other paradigms.
Uses and examples
Functional techniques are widely used for data transformation, concurrency, and domain-specific languages. Common primitives include map, filter, and reduce; function composition and currying are frequent patterns. For more background on the paradigm itself see functional programming.
Practical considerations and distinctions
Functional programming is not mutually exclusive with other styles. Many systems adopt a hybrid approach, using immutable data and pure functions where they help, and controlled side effects when necessary (I/O, stateful interfaces). While reasoning and testing improve, developers should balance abstraction costs, runtime characteristics, and interoperability with existing ecosystems.