Operational Semantics
Operational semantics is a family of methods for defining the meaning of programming languages by specifying the step-by-step execution of programs on an abstract machine. Where denotational semantics asks what a program is — a function from inputs to outputs — operational semantics asks what a program does — how it transforms states, how it consumes resources, how it terminates or fails to terminate. The method is the most concrete and the most intuitive approach to formal semantics, and it is the standard for specifying real programming languages.
The two main styles of operational semantics are small-step and big-step semantics. Small-step semantics defines the meaning of a program as a sequence of atomic reductions: an expression e reduces to e' in one step, and the full meaning of e is the (possibly infinite) sequence of reductions that results from repeated application of the reduction rules. Big-step semantics defines the meaning of a program as a relation between initial states and final results: an expression e evaluates to value v in a single, complete derivation. Small-step semantics is better suited for reasoning about concurrency and non-termination; big-step semantics is simpler and more direct for deterministic, terminating languages.
Operational semantics was developed in the 1960s by John McCarthy and others, and was formalized by Gordon Plotkin in his 1981 structural operational semantics (SOS) framework. Plotkin's insight was that the rules of operational semantics could be given in a structural way: the behavior of a compound expression is determined by the behavior of its subexpressions, and the rules are syntax-directed. This structural approach makes operational semantics amenable to machine checking and provides a natural foundation for type soundness proofs.
The method has been extended to concurrent and distributed systems through labeled transition systems, where transitions are annotated with actions that represent communication or synchronization events. The process calculi — CCS, the π-calculus, and their descendants — use operational semantics as their primary semantic framework, defining the behavior of concurrent agents through transition rules that capture the dynamics of interaction.
Operational semantics is often described as the 'practical' approach to semantics, in contrast to the 'elegant' approach of denotational semantics. This framing is a mistake. Operational semantics is not a compromise with practicality; it is a commitment to the dynamics of computation. A programming language is not a static object that maps inputs to outputs. It is a process that unfolds in time, consuming resources, interacting with its environment, and producing effects that cannot be captured by a pure function. Operational semantics is the only approach that takes this seriously, and the only approach that can serve as the basis for compiler correctness, debugger design, and runtime system construction.