Programming Languages and Translators

Lecture 23: April 22, 2013

The Lambda Calculus

- 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

- 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
`(λx.1)((λx.x x)(λx.x x))`

- 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.

- 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`

.

- 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 →`

; that is,^{*}G(YG)`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.

- 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: `FAC = λn.(... FAC ...)`

- 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
`FAC = G FAC`

- 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`

: `FAC = Y G`

- 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

- 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. f`

^{n}x`n`

has the property that for any lambda expressions`g`

and`y`

,`ngy →* g`

. That is to say,^{n}y`ngy`

causes`g`

to be applied to`y`

*n*times.

- 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`

as follows:^{(m+n)}= f^{m}º f^{n} `λm.λn.λf.λx. m f (n f x)`

- 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

- 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`

(under renaming)

→ λa.λb. true b a

= λa.λb. (λx.λy.x) b a

→ λa.λb. (λy.b) a

→ λa.λb. b

= false

- 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)

- 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.

- 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 `λm.λn.λf.λx. m (n f x)`

- Evaluate
`mul two three`

. - Write a lambda expression for the boolean predicate
`isZero`

and evaluate`isZero one`

.

- 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