COMS W3261
Computer Science Theory
Lecture 19: November 28, 2012
Lambda Calculus I
1. Introduction to Lambda Calculus
- Lambda calculus was introduced in the 1930s by Alonzo Church
as a mathematical system for defining computable functions.
- Lambda calculus is equivalent in definitional power to that of Turing machines.
- Lambda calculus serves as the model for functional programming languages.
- Lisp was developed by John McCarthy in 1956 around lambda calculus.
- ML, a general purpose functional programming language, was developed by
Robin Milner in the late 1970s.
- Haskell, considered by many as one of the purest functional programming languages,
was developed by Simon Peyton Jones, Paul Houdak, Phil Wadler and others in the late 1980s
and early 90s.
2. Grammar for Lambda Calculus
- The central concept in lambda calculus is an expression
which can denote either a function definition (called a function abstraction)
or a function application.
expr → abstraction | application | (expr) | var | constant
abstraction → λ var . expr
application → expr expr
We can think of a lambda-calculus expression as a program which when
evaluated returns a result consisting of another lambda-calculus expression.
3. Function Abstraction
- A function abstraction, often called a lambda abstraction, is an expression
defining a function.
- It consists of a lambda followed by a variable, a period, and then an expression:
λ var . expr
- In the function λ var . expr, var is the formal parameter and expr the body.
- We say λ var . expr binds var in expr.
- Example
- λx.y is a function abstraction.
- The variable x after the λ is the formal parameter of the function.
- The expression y after the period is the body of the function.
4. Function Application
- A function application, often called a lambda application, consists of
an expression followed by an expression.
- Example 1: if e is a function and f an expression, then ef is a function application.
The expression f is the argument of the function e.
- Example 2: in (λx.y)z, we are applying the function λx.y to the
argument z.
5. Disambiguating the Grammar
- The grammar for lambda expressions in section 2 is ambiguous.
- Several conventions overcome the ambiguities:
- Function application is left associative.
- Application binds tighter than period.
- Example 2: (λx. λy. xy) λz.z = (λx. (λy. xy)) λz.z
- The body of a lambda extends as far to the right as possible.
- Example 3: λx.x λy.xyx = λx.(x λy.xyx))
6. Free and Bound Variables
- In lambda calculus all variables are local to function definitions.
- In the function λx.x the variable x in the body of the definition
(the second x) is bound because its first occurrence in the
definition is λx.
- In the expression (λx.xy), the variable x in the
body of the function is bound and the variable y is free.
- In the expression (λx.x)(λy.yx):
- The variable x in the body of the leftmost
expression is bound to the first lambda.
- The variable y in the body of the second expression is bound to the second lambda.
- The variable x in the body of the second expression is free.
- Note that x in second expression is independent of the x in the first expression.
- In the expression (λx.xy)(λy.y):
- The variable y in the body of the leftmost
expression is free.
- The variable y in the body of the second expression is bound to the second lambda.
- Given an expression e, the following recursive rules define FV(e), the set of free variables in e:
- If e is a variable x, then FV(e) = {x}.
- If e is of the form λx.y, then FV(e) = FV(y) - {x}.
- If e is of the form xy, then FV(e) = FV(x) ∪ FV(y).
- An expression with no free variables is said to be closed.
7. Renaming Bound Variables by Alpha Reduction
- The name of the parameter variable in function definition is arbitrary.
We can use any variable to name a parameter, so that the function
λx.x is equivalent to λy.y and λz.z.
This kind of renaming is called alpha reduction.
- Note that we cannot rename free variables in expressions.
- Also note that we cannot change the name of a bound variable in an
expression to conflict
with the name of a free variable in that expression.
8. Evaluation of Function Applications by Beta Reduction
- A function application fg is evaluated by substituting the argument g for the
formal parameter in the body
of the function definition f.
- The notation [y/x]e is used to indicate that y is to be substituted for all free occurrences
of x in the expression e.
- Example: (λx.x)y → [y/x]x = y
- This substitution in a function application is called a beta reduction
and we use a right arrow
to indicate a beta reduction.
- If expr1 → expr2, we say expr1 reduces to expr2 in one step.
- In general, (λx.e)g → [g/x]e means that applying the function
(λx.e) to the argument expression g reduces to the function body [g/x]e
after substituting the argument expression g for the function's formal parameter x
in the function body e.
- A lambda-calculus expression (aka a "program") is "run" by computing a final result by
the application of zero or more beta reductions.
We use →* to denote the reflexive and transitive closure of →.
- Examples
- (λx.x)y → y (illustrating that λx.x is the identity function).
- (λx.xx)(λy.y) → (λy.y)(λy.y) → (λy.y);
thus, we can write (λx.xx)(λy.y) →* (λy.y);.
9. Substitutions
- In standard lambda calculus, the only name for a function is an expression
denoting the function.
- When we want to apply a function to an argument, we write down the whole function definition
and then proceed to evaluate it on the argument.
- As an example, let us apply the identify function to itself:
- (λx.x)(λy.y) → [λy.y/x]x = λy.y which
by alpha reduction is the same as λx.x
- Thus, as expected, the identify function applied to itself yields the identity function.
- When performing substitutions, we should be careful to avoid mixing up free
occurrences of a variable with bound ones.
- When we apply the function λx.e to an expression f, we substitute all
occurrences of x in e with f. If there is a free variable in f named x, we rename
the bound variable x in the function definition to avoid any conflicts before doing
the substitution.
- The rules for substitution are as follows. We assume x and y are distinct variables.
- For variables
- [e/x]x = e
- [e/x]y = y, assuming x ≠ y
- For function applications
- [e/x](f g) = ([e/x]f) ([e/x]g)
- For function abstractions
- [e/x](λx.f) = λx.f
- [e/x](λy.f) = λy.[e/x]f, provided y is not a free variable in e.
- Examples:
- The expression (λx.(λy.xy))y) contains a bound y in the middle
and a free y at the right. We therefore should rename the bound variable y to a new variable,
say z, to evaluate the expression with no name conflicts:
(λx.(λy.xy))y) = (λx.(λz.xz))y) →
[y/x](λz.xz) = (λz.yz)
- The body of the leftmost expression in (λx.(λy.(x(λx.xy))))y is
(λy.(x(λx.xy))). In this body only the first x is free.
Before substituting, we need to rename the bound variable y to
z, say, to avoid confusing it with its free occurrence. Therefore we get the
evaluation:
(λx.(λy.(x(λx.xy))))y = (λx.(λz.(x(λx.xz))))y →
[y/x](λz.(x(λx.xz))) = (λz.(y(λx.xz)))
10. Normal Forms
- An expression containing no more possible beta reductions is called a normal form.
- Any expression not containing a function application in it somewhere is a normal form.
- Examples of normal form expressions:
- x where x is a variable
- xe where x is a variable and e is a normal form expression
- λx.e where x is a variable and e is a normal form expression
- The expression (λz.z z) (λz.z z) 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.
- A remarkable property of lambda calculus is that every expression has a unique
normal form if one exists.
- Lambda calculus is also Church-Rosser, meaning that reductions can be applied in
any order. More formally, if w →* x and w →* y, then there always exists
an expression z such that x →* z and y →* z.
11. Practice Problems
- Evaluate
- (λa.λb.a)c((λd.e)e)
- (λx.a)((λx.xx)((λy.yy))
- (λx.fx)a
- (λy.c((λz.fz)b)
12. References
aho@cs.columbia.edu