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]