Lambda Calculus
2026-08-19 09:45
Truth values, the conditional, NOT/AND/OR.
The natural numbers, with succ, pred and iszero.
But succ steps by one.
To add \(m\) and \(n\) you must apply succ \(n\) times.
The calculus has no loop.
Unit 1 said repetition is recursion.
So define addition recursively and we are done.
We are not.
recThe hardest unit of the module, and the one that completes it.
Iteration — go round again, updating variables.
Needs mutable state. Unavailable.
Recursion — nest calls, each on a smaller problem.
No base case → never stops.
A case that does not shrink → never stops.
Primitive recursion
number of repetitions known
finite nesting depth
= bounded iteration, finite memory
General recursion
number of repetitions unknown
unknown nesting depth
= unbounded iteration, infinite memory
Primitive is strictly weaker.
The structure is unremarkable. You have written recursive functions before.
The problem here is that a recursive definition needs the function to refer to itself.
All names in expressions must be replaced by their definitions before the expression is evaluated.
λx.λy. if iszero y then x else add (succ x) (pred y) ==
λx.λy. if iszero y then x else
((λx.λy. if iszero y then x else add (succ x) (pred y))
(succ x) (pred y)) ==
λx.λy. if iszero y then x else
((λx.λy. if iszero y then x else
((λx.λy. if iszero y then x else add (succ x) (pred y))
(succ x) (pred y)))
(succ x) (pred y)) == ...Replacement will never terminate.
Most languages
a name is a reference
resolved at call time
so a function can name itself
Here
a name is an abbreviation
expanded before evaluation
so it never finishes
We want replacement to happen a finite number of times, depending on the arguments.
But there is no way of knowing the arguments when the function is defined.
Some means of delaying the repetitive use of the function until it is actually required.
Function use always occurs in an application — and can be delayed by abstraction at the point of use.
add1 (succ x) (pred y) has no argument for the bound variable f.
so that add1 gets passed on to subsequent recursions.
Self-reference is now an ordinary argument.
It works, and it is unpleasant.
Every call passes the function to itself explicitly.
Every definition is written in this contorted shape.
A technique, not a solution.
Applicative order — reduce the argument to a value before substituting it.
Pascal’s call by value.
From unit 2: self-application applied to itself reduces forever.
Reduction never reaches the function body…
…where the base case that would have stopped it lives.
We cannot simply hand a function to itself.
The self-application must happen only when needed —
after the conditional has had its chance to select the base case.
A constructor function that builds a recursive function from a non-recursive one, with a single abstraction at the recursion point.
recursive must doPass a copy of its argument to that argument…
…and ensure self-application continues — the copying mechanism must be passed on too.
We have:
The copy mechanism must be an application, and that application must be self-replicating.
\[\lambda s.(s\ s)\]
applied to itself replicates forever.
Forever is too long. We need it to pause and hand control to \(f\).
\[\lambda s.(s\ s)\]
replicates forever
\[\lambda s.(f\ (s\ s))\]
replicates through \(f\)
Each round passes through the conditional — which may pick the base case and stop.
Everything else is bookkeeping.
The calculus now has abstraction, application and unbounded repetition.
There is nothing computable left that it cannot express.
rec notationrecursive is the paradoxical combinator, or fixed point finder.
Called \(Y\) in the λ calculus literature.
The name in the definition is replaced by abstraction, and the combinator applied to the whole defining expression.
rec adds nothing to the language.
Three grammar productions. β, α, η.
Third time this module has done this: sugar in unit 2, notation in unit 3, rec here.
Returns zero when the second is larger — because pred zero is zero.
One of the two subtractions is always zero, so the sum is the real difference.
Count how often the divisor subtracts before the dividend gets smaller.
The interest is in the base cases — where each decides what to do at zero.
Three grammar productions. Three conversion rules.
Selectors, pairs, booleans, the conditional, the naturals, and now full arithmetic.
Every one expands to pure λ terms.
Abstraction and application alone are not obviously enough.
The obstacle was real.
The paradoxical combinator is what removes it.
f f, not f.f.rec is sugar over the combinator, adding nothing to the language.Everything you use — numbers, booleans, conditionals, data structures, recursion — is either primitive for efficiency or definable in the core.
The core has not changed since Church.
When you write if, or a recursive function, the derivation you now know is underneath.
Lambda Calculus · Recursion and Arithmetic