Computer Science Theory

Lecture 20: December 3, 2012

Lambda Calculus II

- Evaluation strategies
- Properties of the lambda calculus
- Multiple-argument functions
- Implementing programming language constructs in lambda calculus
- Recursion with the Y combinator

- A subexpression of a lambda expression where a lambda can be applied to an argument is called a redex (short for reducible expression).
- If there is more than one redex in an expression, there can be several evaluation orders for an expression.
- For example, the expression (+ (* 1 2) (* 3 4)) has two redexes: (* 1 2) and (* 3 4) and the expression can be evaluated by evaluating either redex first.
- Lambda calculus uses two basic evaluation orders: normal order and applicative order.
- Normal order evaluation
- We always reduce the leftmost redex of the outermost redex at each step.
- The expression (λy.λz.z)((λx.x x)(λx.x x)) can be reduced to the normal form λz.z by first applying the function (λy.λz.z) to the argument ((λx.x x)(λx.x x)). This reduction order, reducing the leftmost outermost redex, follows normal form evaluation.
- This corresponds to call by name as in Algol 60.
- If an expression has a normal form, then normal order evaluation will always find it.
- Normal order evaluation is sometimes known as lazy evaluation and is the usual order of evaluation.
- Applicative order evaluation
- Here we always reduce the leftmost innermost redex.
- This corresponds to call by value as in the programming language C.
- In applicative order evaluation actual parameters are evaluated before being passed to a function. Both the function and the argument are reduced before the argument is substituted into the body of the function.
- Even though an expression may have a normal form, applicative order evaluation may fail to find it.
- For example, in the expression (λy.λz.z)((λx.x x)(λx.x x)) if we first try to reduce the innermost redex ((λx.x x)(λx.x x)), we discover it never terminates: it always evaluates to itself.
- Applicative order is sometimes called eager evaluation.

- Lambda calculus is Turing-complete: We can translate any Turing-machine program into an equivalent lambda-calculus program and vice versa.
- Lambda calculus is the computational model underlying functional programming languages such as ML and Haskell. We can construct lambda calculus expressions that simulate integers, booleans, logic, loops, data structures, etc.
- Church-Rosser properties
- No lambda expression can have more than one normal form. This is a corollary of the first Church-Rosser theorem:
- Church-Rosser Theorem 1: If expression w can be reduced to one expression x and and to another expression y, then there always exists an expression z such that x →* z and y →* z.
- The second Church-Rosser theorem guarantees that normal order reducion will always find a normal form if it exits:
- Church-Rosser Theorem 2: If expression w can be reduced to a normal form expression x, then there is a normal order reduction from w to x.

- Currying
- In lambda calculus, every function has one argument. We represent multiple-argument functions using a technique called currying where we represent a two-argument function (f x y) by two applications of one-argment functions ((f x) y).
- Abbreviating expressions representing function with multiple arguments
- Sometimes a function of two arguments such as (λs.(λz.z)) will be abbreviated as λsz.z. The convention is that s is the first formal parameter and z the second. As we shall soon see, this function represents the integer zero.
- This convention can be used for functions with more than two arguments. The expression λwyx.y(wyx) abbreviates the three-parameter function λw.λy.λx.y(wyx) which we shall see represents the successor function.

- We can construct pure lambda calculus expressions (expressions with no constants) to represent programming language constructs such as integers, booleans, arithmetic operations, logical operations, recursion, etc.
- Integers
- The integers can be represented using a function to represent zero and using a successor function "succ(0)" to represent 1, "succ(succ(0))" to represent 2, and so on.
- 0 is represented by the function λs.λz.z
- 1 is represented by the function λs.λz.s(z)
- 2 is represented by the function λs.λz.s(s(z))
- In lambda calculus we cannot name functions -- we need to repeat the text of a function every time we use it. For notation convenience we will use
- 0 as a synonym for the string λs.λz.z
- 1 as a synonym for the string λs.λz.s(z) and so on.
- The successor function
- We define the successor function as λw. λy. λx. y(w y x).
- Applying the successor function to zero, we get
- (λw. λy. λx. y(w y x))(λs.λz.z)

→ λy. λx. y((λs.λz.z) y x)

// Here we substituted the argument (λs.λz.z) for all occurrences of w in the body y(w y x)

→ λy. λx. y((λz.z)x)

// Here we substituted the argument y (following the function for 0) for s in the body λz.z

// Since there is no s in the body λz.z the substitution returns the body λz.z

→ λy.λx.y(x)
// Here we substituted
the argument
x for z in the body z of the function λz.z
- The last expression represents 1.
- Addition
- We can define add as
- λm. λn. λf. λx. m f (n f x)
- Multiplication
- We can define multiply as
- λm. λn. λf. m (n f)
- Logic
- The boolean value true can be represented by a function that always selects the first argument:
- true = λx.λy.x
- The boolean value false can be represented by a function that always selects the second argument:
- false = λx.λy.y
- The conditional if p then a else b can be represented by
- if-then-else = λp.λa.λb. p a b
- The logic operators and, or, not can be expressed with if-then-else:
- and = λp.λq. p q p
- or = λp.λq. p p q
- not = λp.λa.λb. p b a
- Equality and inequality tests can be defined analogously.

- Recursive functions can be defined in the lambda calculus using the fixed-point Y combinator, which is a function that takes a function G as an argument and returns G(Y G). That is, Y G = G(Y G).
- With repeated applications we can get G(G(Y G)), G(G(G(Y G))),... .
- The Y combinator has a simple definition:
- Y = λf. (λx. f(x x))(λx. f(x x))
- Note that
- Y G = (λf.(λx.f(xx))(λx.f(xx)))G
- → (λx.G(xx))(λx.G(xx))
- → G((λx.G(xx))(λx.G(xx)))
- = G(Y G)
- The last line follows from Y G = (λx.G(xx))(λx.G(xx))

- Evaluate (succ (succ (succ 0))).
- Evaluate (add 2 3).
- Evaluate (mult 2 3).
- Define a function that tests if its argument is zero.
- Use the Y combinator to sum the integers from 0 to 3.

- Simon Peyton Jones,
*The Implementation of Functional Programming Languages*, Prentice-Hall, 1987. - Stephen 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