COMS W4115
Programming Languages and Translators
Lecture 23: April 22, 2013
The Lambda Calculus
Outline
- Reduction orders
- The Church-Rosser theorems
- The Y combinator
- Implementing factorial using the Y combinator
- Church numerals
- Arithmetic
- Logic
- Other programming language constructs
- The influence of the lambda calculus on functional languages
1. Reduction Orders
- Programming languages use many different techniques to pass parameters to procedures
such as call by value, call by reference, call by value-return, call by name, and so on.
We can model many of these parameter-passing mechanisms using the lambda calculus.
- The order in which reductions are applied within a lambda expression
can affect the final result.
We will use the terms reduction order and evaluation order synonymously.
- A reducible expression (redex) in a lambda expression is a subexpression
that can be reduced using beta reduction.
- An expression that contains no redexes is said to be in normal form.
- Some reduction orders for an expression may yield a normal form
expression while
other orders may not. For example, consider the expression
- This expression has two redexes:
- The entire expression is a redex in which we can apply the function
(λx.1)
to
the argument
((λx.x x)(λx.x x))
to yield the value 1.
- The rightmost subexpression
((λx.x x)(λx.x x))
is also a redex in which
we can apply the function (λx.x x)
to the argument
(λx.x x)
. But if we do this reduction we get same subexpression:
(λx.x x)(λx.x x)
→
(λx.x x)(λx.x x)
.
Thus, continuing this order of evaluation will not terminate in a normal form.
- A remarkable property of lambda calculus is that every lambda expression
has a unique normal form if one exists.
- The expression
(λx.1)((λx.x x)(λx.x x))
has the normal form 1.
- The expression
(λx.x x)(λx.x x)
does not have a normal form
because it always evaluates to itself. We can think of this expression as
a representation for an infinite loop.
- There are two common reduction orders for lambda expressions:
normal order evaluation and applicative order evaluation.
- Normal order evaluation
- In normal order evaluation we always reduce the leftmost outermost redex at each step.
- The first reduction order above is a normal order evaluation.
- If an expression has a normal form, then normal order evaluation will always find it.
- Applicative order evaluation
- In applicative order evaluation we always reduce the leftmost innermost redex
at each step.
- The second reduction order above is an applicative order evaluation.
- Thus, even though an expression may have a normal form, applicative order evaluation
may fail to find it.
2. The Church-Rosser Theorems
- A remarkable property of lambda calculus is that every expression has a unique
normal form if one exists.
- Church-Rosser Theorem I: If
e →* f
and e →* g
by any two reduction orders,
then there always exists
a lambda expression h
such that f →* h
and g →* h
.
- A corollary of this theorem is that no lambda expression can be reduced to
two distinct normal forms. To see this, suppose
f
and g
are in normal form. The Church-Rosser theorem says there must be an expression
h
such that f
and g
are each reducible
to h
. Since f
and g
are in normal form,
they cannot have any redexes so f = g = h
.
- This corollary says that all reduction sequences that terminate will always
yield the same result and that result must be a normal form.
- The term confluent is often applied to a rewriting system that has the
Church-Rosser property.
- Church-Rosser Theorem II: If
e →* f
and
f
is in normal form, then there exists a normal order
reduction sequence from e
to f
.
3. The Y Combinator
- The
Y
combinator (sometimes called the paradoxical combinator)
is a function that takes a function
G
as an argument and returns G(YG)
.
With repeated applications we can get
G(G(YG)), G(G(G(YG))),...
.
- We can implement recursive functions using the
Y
combinator.
Y
is defined as follows:
(λf.(λx.f(x x))(λx.f(x x)))
- Let us evaluate
YG
where G
is any expression:
(λf.(λx.f(x x))(λx'.f(x' x'))) G
→ (λx.G(x x))(λx'.G(x' x'))
→ G((λx'.G(x' x'))(λx'.G(x' x')))
↔ G((λf.(λx.f(x x))(λx.f(x x)))G)
= G(YG)
- Thus,
YG →* G(YG)
;
that is, YG
reduces to a call of G
on
(YG)
.
- We will use
Y
to implement recursive functions.
Y
is an example of a fixed-point combinator.
4. Implementing Factorial using the Y Combinator
- If we could name lambda abstractions, we could define the
factorial function with the following recursive definition:
FAC = (λn.IF (= n 0) 1 (* n (FAC (- n 1 ))))
- where
IF
is a conditional function.
- However, functions in lambda calculus cannot be named; they
are anonymous.
- But we can express recursion as the fixed-point of a function
G
.
To do this, let us simplify the essence of the problem.
We begin with a skeletal recursive definition:
- By performing beta abstraction on
FAC
, we can transform its
definition to:
FAC = (λf.(λn.(... f ...))) FAC
= G FAC
- where
G = λf.λn.IF (= n 0) 1 (* n (f (- n 1 )))
- Beta abstraction is just the reverse of beta reduction.
- The equation
- says that when the function
G
is applied to FAC
,
the result is FAC
.
That is, FAC
is a fixed-point of G
.
- We can use the Y combinator to implement
FAC
:
- As an example, let compute
FAC 1
:
FAC 1 = Y G 1
= G (Y G) 1
= λf.λn.IF (= n 0) 1 (* n (f (- n 1 ))))(Y G) 1
→ λn.IF (= n 0) 1 (* n ((Y G) (- n 1 ))))1
→ IF (= n 0) 1 (* n ((Y G) (- 1 1 )))
→ * 1 (Y G 0)
= * 1 (G(Y G) 0)
= * 1((λf.λn.IF (= n 0) 1 (* n (f (- n 1 ))))(Y G 0)
→ * 1((λn.IF (= n 0) 1 (* n ((Y G) (- n 1 ))))0
→ * 1(IF (= 0 0) 1 (* 0 ((Y G) (- 0 1 )))
→ * 1 1
→ 1
5. Church Numerals
- Church numberals are a way of representing the integers in lambda calculus.
- Church numerals are defined as functions taking two parameters:
0
is defined as λf.λx. x
1
is defined as λf.λx. f x
2
is defined as λf.λx. f (f x)
.
3
is defined as λf.λx. f (f (f x))
.
n
is defined as
λf.λx. fn x
n
has the property that for any lambda expressions g
and
y
, ngy →* gny
.
That is to say, ngy
causes g
to be
applied to y
n times.
6. Arithmetic
- In lambda calculus, arithmetic functions can be represented by
corresponding operations on Church numerals.
- We can define a successor function
succ
of three arguments that adds one
to its first argument:
λn.λf.λx. f (n f x)
- Example: Let us evaluate
succ 0
:
(λn.λf.λx. f (n f x)) 0
→ λf.λx. f (0 f x)
= λf.λx. f ((λf.λx. x) f x)
→ λf.λx. f (λx. x) x)
→ λf.λx. f x
= 1
- We can define a function
add
using the identity
f(m+n) =
fm º fn
as follows:
- Example: Let us evaluate
add 1 2
:
λm.λn.λf.λx. m f (n f x) 1 2
→ λn.λf.λx. 1 f (n f x) 2
→* λf.λx. f (f (f x))
= 3
7. Logic
- The boolean value true can be represented by a function of two arguments that
always selects its first argument:
λx.λy.x
- The boolean value false can be represented by a function of two arguments that
always selects its second argument:
λx.λy.y
- An if-then-else statement can be represented
by a function of three arguments
λc.λi.λe. c i e
that uses its
condition c
to select either the if-part i
or the else-part e
.
- Example: Let us evaluate if true then 1 else 2:
(λc.λi.λe. c i e) true 1 2
→ (λi.λe. true i e) 1 2
→ (λe. true 1 e) 2
→ true 1 2
= (λx.λy.x) 1 2
→ (λy.1) 2
→ 1
- The boolean operators and, or, and not can be implemented as follows:
and = λp.λq. p q p
or = λp.λq. p p q
not = λp.λa.λb. p b a
- Example: Let us evaluate
not true
:
(λp.λa.λb. p b a) true
→ λa.λb. true b a
= λa.λb. (λx.λy.x) b a
→ λa.λb. (λy.b) a
→ λa.λb. b
= false
(under renaming)
8. Other Programming Language Constructs
- We can readily implement other programming language constructs in lambda calculus.
As an example, here are lambda calculus expressions for various list operations
such as
cons (constructing a list),
head (selecting the first item from a list), and
tail (selecting the remainder of a list after the first item):
cons = λh.λt.λf. f h t
head = λl.l (λh.λt. h)
tail = λl.l (λh.λt. t)
9. The Influence of The Lambda Calculus on Functional Languages
- Our next lecture will be by Maria Taku who will talk about the influence
of the lambda calculus on functional languages and her experiences
implementing the PLT compiler project using OCaml.
10. Practice Problems
- Evaluate
(λx.((λw.λz. + w z)1))
((λx. xx)(λx. xx)))
((λy. * y 1) (- 3 2))
using normal order evaluation and applicative order evaluation.
- Give an example of a code optimization transformation that has the Church-Rosser property.
- Evaluate
FAC 2
.
- Evaluate
succ two
.
- Evaluate
add two three
.
- Let
mul
be the function
- Evaluate
mul two three
.
- Write a lambda expression for the boolean predicate
isZero
and evaluate isZero one
.
11. References
- Simon Peyton Jones, The Implementation of Functional Programming Languages,
Prentice-Hall, 1987.
-
Stephen A. Edwards: The Lambda Calculus
- http://www.inf.fu-berlin.de/lehre/WS01/ALPI/lambda.pdf
- http://www.soe.ucsc.edu/classes/cmps112/Spring03/readings/lambdacalculus/project3.html
aho@cs.columbia.edu