Why substitution works

If an expression depends only on its explicit inputs and evaluation has no additional observable effect, substituting its determined value preserves meaning. Replacing 2 + 3 with 5 is a simple example.

Reading changing external state, updating shared variables or producing output can invalidate replacement based solely on a return value. Two calls coincidentally producing the same number do not establish transparency.

References: [1]

Equations and evaluation strategy

Transparency supports reasoning with equations instead of tracking hidden assignment history. Haskell gives expressions static types and non-strict semantics while leaving compilation and interpretation strategies to implementations.

It does not license arbitrary eager evaluation: divergence, errors and the language’s strictness rules still matter. Equational reasoning does not itself establish termination or validate every proposed optimization.

References: [2]

Values and performed actions

Haskell’s IO represents actions as composable values, separating an action’s description from its execution. Sequencing specifies their order; purity of the expression language does not merge two performed actions into one.

A pure functional program can therefore read files or produce output. The issue is how effects are represented and what a substitution preserves, not whether the outside world changes.

References: [3]