Reduction strategies

The rule that picks the next redex among all the redexes of a term is called a reduction strategy. Let us see where the choice arises. A term has one of three forms:

  • a variable xx — nothing to reduce;
  • an abstraction λx.M\lambda x.M — no choice: we reduce the body MM;
  • an application MNM\,N — redexes may sit both in the left part and in the right one; choosing between them is exactly what a strategy does.

Let us unfold an application: its left part may itself be an application, the left part of that one too, and so on. Descending along the left branches to the first non-application — the head of the term — we get one of two shapes:

(((xN1)N2)Nk),((((λx.M)N1)N2)Nk).\begin{aligned}&(\dots((x\,N_1)\,N_2)\dots N_k), \\&(\dots(((\lambda x.M)\,N_1)\,N_2)\dots N_k).\end{aligned}

In the first shape the head is a variable xx: there is nothing to contract on the left, redexes can hide only in the arguments NiN_i. In the second the head is an abstraction, and (λx.M)N1(\lambda x.M)\,N_1 is a redex; here is the fork: contract it right away or evaluate the argument first. This choice is what distinguishes the two classic strategies.

The normal strategy contracts the leftmost outermost redex — in our notation (λx.M)N1(\lambda x.M)\,N_1: arguments are substituted into the body unevaluated. The applicative strategy does the opposite: it first brings the argument of the redex to normal form and only then contracts the redex itself.

A term is conveniently drawn as a tree. For example, for ((λx.M)N1)N2((\lambda x.M)\,N_1)\,N_2 it looks like this (fig. 1.4): the @@ nodes are applications, the λx\lambda x node is an abstraction. The leftmost outermost redex is found on the tree by descending from the root along the left edges: it is the first @@ whose left child is an abstraction.

Tree of the term ((λx.M) N₁) N₂: two application nodes @ and an abstraction node λx; the lower @ is the leftmost outermost redex
Fig. 1.4. The tree of the term ((λx.M)N1)N2((\lambda x.M)\,N_1)\,N_2: the @@ nodes are applications, the λx\lambda x node is an abstraction; the red @@ is the leftmost outermost redex (λx.M)N1(\lambda x.M)\,N_1.

The same descent lets us introduce an important notion — the head normal form. Let us explain it with two examples:

λx1xn.yN1Nk,n,k0,λx1xn.(λz.M)N1Nk,n0, k>0.\begin{aligned}&\lambda x_1\dots x_n.\,y\,N_1\dots N_k, \quad n,k \ge 0,\\&\lambda x_1\dots x_n.\,(\lambda z.M)\,N_1\dots N_k, \quad n\ge 0,\ k>0.\end{aligned}

The first term is a head normal form: the descent along the left branches ends at the variable yy, called the head variable. The head of such a term will never change; redexes can hide only in the arguments NiN_i. The second term is not a head normal form: its head is the redex (λz.M)N1(\lambda z.M)\,N_1, called the head redex.

Let us state both strategies precisely, as rules of an operational semantics. As if we had not enough notions already — we need three more, three syntactic categories. The first is the non-abstraction NA\mathit{NA}:

NA::=xMN.\mathit{NA} \mathrel{\text{::=}} x \mid M\,N.

Here everything seems clear: it is any term except an abstraction. MM and NN are arbitrary terms, so a non-abstraction may well be a redex — for example (λy.y)z(\lambda y.y)\,z. The other two categories describe finished results: normal forms NF\mathit{NF} and their subclass, non-abstraction normal forms NANF\mathit{NANF}:

NF::=λx.NFNANF,NANF::=xNANF NF.\begin{aligned}\mathit{NF} &\mathrel{\text{::=}} \lambda x.\mathit{NF} \mid \mathit{NANF},\\\mathit{NANF} &\mathrel{\text{::=}} x \mid \mathit{NANF}\ \mathit{NF}.\end{aligned}

The split is necessary: without it we could form an application with an abstraction on the left — and such a term is not a normal form any more, it is a redex. Examples of NF\mathit{NF}: λx.x\lambda x.\,x, λf.λx.fx\lambda f.\lambda x.\,f\,x. Examples of NANF\mathit{NANF}: xx, fxf\,x, x(λy.y)zx\,(\lambda y.y)\,z — and each of them is at the same time an NF\mathit{NF}.

Now, at last, the strategies. At the heart of each lies its own redex-contraction rule. The normal strategy uses ordinary β\beta-reduction: the redex is contracted right away, the argument NN is an arbitrary term and is substituted into the body unevaluated:

(λx.M)N\displaystyle (\lambda x.M)\,N M[x ⁣:= ⁣N]\displaystyle \to M[x\!:=\!N]
(1.33)

The applicative strategy contracts a redex differently — only once the argument has been evaluated, that is, brought to normal form — to the left of the arrow stands an NF\mathit{NF}, and until the argument is one, contraction is forbidden:

(λx.M)NF\displaystyle (\lambda x.M)\,\mathit{NF} M[x ⁣:= ⁣NF]\displaystyle \to M[x\!:=\!\mathit{NF}]
(1.34)

Since we cannot contract a redex while the argument is not in normal form, the applicative strategy has a rule for the argument:

NN(λx.M)N(λx.M)N\dfrac{N \to N'}{(\lambda x.M)\,N \to (\lambda x.M)\,N'}
(1.35)

The remaining rules are shared by the normal strategy and the applicative one:

NANANANNAN,NNNANFNNANFN,MMλx.Mλx.M\dfrac{\mathit{NA} \to \mathit{NA}'}{\mathit{NA}\,N \to \mathit{NA}'\,N}, \quad \dfrac{N \to N'}{\mathit{NANF}\,N \to \mathit{NANF}\,N'}, \quad \dfrac{M \to M'}{\lambda x.M \to \lambda x.M'}
(1.36)

The first rule reduces the left part of an application while it remains a non-abstraction; the second moves on to the argument only when the left part is already a NANF\mathit{NANF}; the third takes reduction into the body of an abstraction. The categories NA\mathit{NA} and NANF\mathit{NANF} in the premises are exactly what guarantees the leftmost outermost order: while there is something to contract on the left, we do not look to the right.

The choice of the leftmost outermost redex is no accident: the normalization theorem (Curry) guarantees that if a term has a normal form, then successively contracting exactly such a redex will reach it. For this the normal strategy is called complete.

Completeness is not free: an argument substituted into several places has to be recomputed. For a “big” NN: (λx.Fx(Gx)x)N(\lambda x.\,F\,x\,(G\,x)\,x)\,N βFN(GN)N\to_\beta F\,N\,(G\,N)\,N — here NN has to be reduced three times — once per copy. The applicative strategy evaluates NN exactly once, but pays for this with completeness. Let K\mathrm{K} λxy.x\equiv \lambda x y.x, I\mathrm{I} λx.x\equiv \lambda x.x, and let Ω\Omega (λx.xx)(λx.xx)\equiv (\lambda x.x\,x)\,(\lambda x.x\,x) — a term with no normal form. The normal strategy computes KIΩ\mathrm{K}\,\mathrm{I}\,\Omega βI\twoheadrightarrow_\beta \mathrm{I}, discarding Ω\Omega without a glance; the applicative one first takes on the argument Ω\Omega — and loops forever.

In programming languages the two strategies live under different names. The applicative one is the call-by-value of strict languages such as C: arguments first, then the application. The normal one underlies lazy languages such as Haskell: call-by-name.