Programming Languages and Translators

Lecture 25: April 23, 2014

The Lambda Calculus - II

- 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

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

- The lambda calculus is the programming model for functional languages such as Haskell, ML, and OCaml.
- Constructs such as lambda expressions have appeared in many other languages.
- Here are three examples of anonymous functions in lambda form from Python.
The functions
`map()`

and`filter()`

are built-in functions in Python. - Function of two arguments that returns their product:
`>>> print (lambda x, y: x*y)(2, 3)`

6

- Using
`map()`

apply a function that squares every member of a list: `>>> squares = map(lambda x: x**2, range(5))`

>>> print squares

[0, 1, 4, 9, 16]

- Using
`map()`

and`filter()`

print all squares greater than 5 and less than 50: `>>> squares = map(lambda x: x**2, range(8))`

>>> squares_in_range = filter(lambda x: x > 5 and x < 50, squares)

>>> print squares_in_range

[9, 16, 25, 36, 49]

- 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
- Python Tutorial: 4.7.5 Lambda Expressions
- Bogotobogo Python Functions Tutorial

aho@cs.columbia.edu