Unit content
Observational equivalence
Two program fragments can have different syntax yet be indistinguishable to every surrounding program. Observational equivalence captures this idea of “same behavior from the outside.”
Let $e_1$ and $e_2$ have the same interface. Informally,
$$e_1 \approx e_2$$
when every allowed program context $C[;]$ observes the same relevant behavior from $C[e_1]$ and $C[e_2]$.
What counts as an observation depends on the language: final values, termination, printed output, exceptions or interaction with mutable state may all matter.
For pure arithmetic,
$$2+3 \approx 5$$
because replacing one with the other cannot change a program's result. With side effects, however,
(print("x"); 5)
is not equivalent to 5 if output is observable, even though both produce the same numeric value.
Observational equivalence is stronger and more useful than textual equality. It is the correctness criterion behind many optimizations and refactorings: a transformation is semantics-preserving when replacing the original fragment by the transformed one cannot be detected by any relevant program context.