Functional Programming Core
A program becomes difficult to reason about when reading an expression is not enough to know what it changes. Functional programming reduces that uncertainty by organizing code around values and transformations while keeping effects visible at controlled boundaries.
Pure Transformations
A pure function returns the same result for the same inputs and does not change externally visible state. This gives it referential transparency: a call can be replaced by its result without changing the program’s meaning.
normalize(input) → normalized value
This is easier to test and compose than an operation that also mutates global state, writes a file, and sends a message.
Immutable Data
With immutable data, an operation produces a new value instead of changing the old one in place. Persistent data structures make this practical by sharing unchanged structure rather than copying everything.
Immutability does not mean a program performs no work or maintains no state. It means state transitions are represented explicitly, making the before and after values easier to inspect.
Functions as Values
Higher-order functions accept functions as arguments or return them as results. This supports reusable operations such as mapping, filtering, folding, and composition.
Composition connects small transformations so the output of one becomes the input of another. Currying and partial application are techniques for building specialized functions from more general ones.
Expressions, Recursion, and Laziness
Expression-oriented code computes values instead of relying mainly on sequences of state-changing statements. Recursion and folds can describe repeated transformations, although practical languages may also use loops when they are clearer or more efficient.
Lazy evaluation delays work until its result is needed. It can enable reusable pipelines and potentially unbounded sequences, but it can also delay errors and make resource use less obvious. Nix language basics shows laziness in a concrete configuration language.
Effects Still Exist
Useful software performs input, output, mutation, concurrency, and failure handling. Functional designs do not erase these effects; they separate pure decision-making from the code that interacts with the outside world.
A common production shape is a functional core surrounded by an imperative shell:
read input → pure decisions → perform effects
This boundary lets most rules remain deterministic while the outer layer handles databases, networks, clocks, and user interfaces.