# The Lambda Calculus¶

## The Lambda Calculus¶

The \(λ\)-calculus is a mathematical language of **lambda terms** bound by a set of
transformation rules. The \(λ\)-calculus notation was introduced in the 1930s by Alonzo
Church.

Just like programming languages, the \(λ\)-calculus has rules for what is a valid syntax:

Variables: | A variable (such as \(x\)) is valid term in the \(λ\)-calculus. |
---|---|

Abstractions: | If \(t\) is a term and \(x\) is a variable, then the term \(λx.t\) is a lambda abstraction. |

Applications: | If \(t\) and \(s\) are terms, then \(ts\) is the application term of \(t\) onto \(s\). |

## Anonymous Functions¶

**Lambda abstractions** can be thought of as anonymous functions in the \(λ\)-calculus.

A lambda abstraction which takes an \(x\) and returns a \(t\) is written as so:

Example

Suppose in mathematics we define a function \(f(x) = x + 2\). This could be written as \((λ x.x + 2)\) in the \(λ\)-calculus [1]. Of course, this function is anonymous and not bound to the name \(f\).

[1] | Of course, we haven’t said that either \(+\) nor \(2\) is valid in lambda calculus yet. We will get to that… |

## Functions are First Class¶

In the \(λ\)-calculus, abstractions are not only first class functions, they are our only way of to encode data.

Abstractions are used to encode everything:

- Numbers
- Booleans (true/false)
- Conses
- …

## Currying¶

Since abstractions in the \(λ\)-calculus may only take one argument, currying is typically used to denote functions of multiple arguments. For example, the function \(f(x, y) = x\) might be written as:

Further, function application is left-associative, so \(fxy\) means \((fx)y\).

## Free and Bound Variables¶

The \(λ\) operator (which creates lambda abstractions) binds a variable to wherever it occurs in the expression.

- Variables which are bound in an expression are called
**bound variables** - Variables which are not bound in an expression are called
**free variables**

Example

With your learning group, identify the free and bound variables in this expression:

## Transformations¶

\(\alpha\)-conversion: | |
---|---|

Allows variables to be renamed to non-colliding names. For example, \(λx.x\) is \(\alpha\)-equivalent to \(λy.y\). | |

\(\beta\)-reduction: | |

Allows functions to be applied. For example, \((λx.λy.x)(λx.x)\) is \(\beta\)-equivalent to \(λy.(λx.x)\). | |

\(\eta\)-conversion: | |

Allows functions with the same external properties to be substituted. For example, \((λx.(fx))\) is \(\eta\)-equivalent to \(f\) if \(x\) is not a free variable in \(f\). |

## Examples: Alpha Equivalence¶

With your learning group, identify if each of the following are a valid \(\alpha\)-conversion. Turn in your answers on a sheet of paper with all of your names at the end of class for learning group participation credit for today.

- \(λx.λx.x \to λy.λy.y\)
- \(λx.λx.x \to λy.λx.x\)
- \(λx.λx.x \to λy.λx.y\)
- \(λx.λy.x \to λy.λy.y\)

## Examples: Beta Reductions¶

Fully \(\beta\)-reduce each of the following expressions:

- \((λx.λy.λf.fxy)(λx.λy.y)(λx.λy.x)(λx.λy.y)\)
- \((λa.λb.a(λb.λf.λx.f(bfx))b)(λf.λx.fx)(λf.λx.f(fx))\)

## Church Numerals¶

Since all data in the \(λ\)-calculus must be a function, we use a clever convention of
functions (called **Church numerals**) to define numbers:

0: | \(λf.λx.x\) |
---|---|

1: | \(λf.λx.fx\) |

2: | \(λf.λx.f(fx)\) |

3: | \(λf.λx.f(f(fx))\) |

… and so on. In fact, the successor to any number \(n\) can be written as:

Notice this

Defining numbers as functions in this way allows us to apply a Chuch numeral \(n\) to a function to get a new function that applies the original function \(n\) times.

## Shorthand Notations¶

While it’s not a defined part of the \(λ\)-calculus, we define common shorthands for some features:

- \(0, 1, 2, \ldots\) are shorthand for their corresponding Church numerals
- \(\{\text{SUCC}\} = λn.λf.λx.f(nfx)\)

Note

The notation “\(=\)” above is not a part of the \(λ\)-calculus. I’m using it for saying “is shorthand for”.

## Addition and Multiplication¶

Adding \(m\) to \(n\) can be thought of as taking the successor to \(n\), \(m\) times. Using our shorthand \(\text{SUCC}\), this can be written as:

Similarly, multiplying \(m\) by \(n\) can be thought of as repeating \(\text{ADD}\, n\), \(m\) times and then applying it to \(0\), this can be written as:

## Boolean Logic¶

We use the following convention for true and false:

From here, we can define some common boolean operators:

## Cons Cells¶

By convention, we will represent a cons cell as a function that applies its argument to the CAR and CDR of the cons cell. This leads to the shorthand:

Using this, we can define lists:

## Lambda Calculus Computational Model¶

## Beta Reduction Algorithm (Eager)¶

To \(\beta\)-reduce a term \(t\):

- If \(t\) is a variable, it is already reduced. Return \(t\).
- If \(t\) is an application:
- \(\beta\)-reduce the left hand and right hand sides and let \(m\) and \(n\) be the results, respectively.
- If \(m\) is an abstraction, return the beta reduction of \(m\)’s term with all instances of \(m\)’s variable replaced with \(n\).
- Otherwise, return the application of \(m\) onto \(n\).

- If \(t\) is an abstraction, \(\beta\)-reduce the term of the abstraction and return the abstraction.

## Lazy Algorithm Motivation¶

While the eager algorithm will produce the correct results, we may end up evaluating terms we did not need to. For example, consider:

We don’t want to have to spend all of that work evaluating \(x\) if we did not need to!

## Reducible Expressions¶

Definition

A **reducible expression** (called *redex* for short) is an application with
an abstraction as its left child.

In order to implement lazy evaluation, we will need to concern ourselves with
the **left-most** of the **outer-most** redexes.

A redex is outer-most if it can be reached from the root of the tree without going through another redex.

## Beta Reduction Algorithm (Lazy)¶

To lazy \(\beta\)-reduce a term:

- Find the left-most of the outer-most redexes in the abstract syntax tree and preform a substitution to complete the application.
- If any redexes remain, go to step 1.

## Is substitution always safe?¶

Consider the following (naïve) substitution procedure for a term \(t\) for a variable \(v\) with replacement \(r\):

- If \(t\) is an
**application**, substitute \(v\) for \(r\) onto the left and right sides. - If \(t\) is an
**abstraction**whose variable is \(v\), return the abstraction unaltered. - If \(t\) is an
**abstraction**whose variable is not \(v\), return the abstraction with \(v\) substituted for \(r\) onto the abstraction’s term. - If \(t\) is a
**variable**which is \(v\), return \(r\). - If \(t\) is a
**variable**which is not \(v\), return \(v\).

*What could possibly go wrong?*

## Alpha Renames in Substitutions¶

Suppose we wish to \(\beta\)-reduce the following term:

Using the previous (simple) rules of substitution with our \(\beta\)-reduction algoritm, we end up with this:

**Oops.** The binding changed! These terms are *not* equivalent.

**How to solve?** \(\alpha\)-rename.

## Alpha Renaming Condition¶

It is sufficient to say that we need to preform an \(\alpha\)-rename in order to preform a substitution of \(v\) for \(r\) in \(t\) if all of the below are true:

- \(t\) is an abstraction.
- \(t\)’s variable is not \(v\).
- \(t\)’s variable appears at least once as a free variable in \(r\).

(revisit example on previous slide)

## Lambda Calculus: Where from Here?¶

- Subtraction is hard, but doable. Check out the Wikipedia page on Church Numerals for more info.
- For recursion, we need to reference ourselves in a lambda abstraction. This is done using a Y-combinator.
- From there, we can use the \(λ\)-calculus to compute the solution to any problem that a Turing machine can.
- More on all of this in CSCI-561 (Theory of Computation).
- Many functional programming languages (e.g., Haskell, Scheme, SlytherLisp) are just practical implementations of the \(λ\)-calculus.