Lecture 17 — 2018-03-20

Lambda calculus: definition

This lecture is written in literate Haskell; you can download the raw source.

We defined the lambda calculus, originated by Alonzo Church in the 1930s, worked on by some other famous folks (Stephen Kleene, Haskell Curry Alan Turing). Here are the collected definitions.

The lambda calculus

Let V be an infinite set of variable names.

We define the terms or expressions of the lambda calculus as follows:

e in LC ::= x | e1 e2 | lambda x. e

Note that application is left associative, so:

x y z = (x y) z

And that we write multiple variables after a lambda to indicate nesting:

lambda x y z. e = lambda x. (lambda y. (lambda z. e))

Free variables and substitution

We define the free variables of a term using the following function:

fv :: LC -> 2^V
fv(x) = {x}
fv(e1 e2) = fv(e1) U fv(e2)
fv(lambda x. e) = fv(e) \ {x}

We say x is free in e if x is in fv(e). If a variable occurs in e and it isn’t free, we say it is bound. Every occurrence of a bound variable appears under a lambda with that variable (prove this to yourself!). For example:

fv(lambda x y z. q (x y)) = {q}

We say e is closed when fv(e) = {}. In general, we will only want to think about closed terms—it’s a strange idea to run a program with free variables! If a term isn’t closed, it’s open.

Next, we define a substitution operation. We’ll use a simple function notation first, reading subst(e1,x,e2) as “substitute e2 for x in e1”:

subst :: LC -> V -> LC -> LC
subst(x,x,e2)            = e2
subst(y,x,e2)            = y
subst(e11 e12,x,e2)      = subst(e11,x,e2) subst(e12,x,e2)
subst(lambda x. e1,x,e2) = lambda x. e1
subst(lambda y. e1,x,e2) = lambda y. subst(e1,x,e2)

We typically write e1[e2/x] (read “e1 with e2 for x”) to mean subst(e1,x,e2).

Equational reasoning

Now we can give the two rules that define how the lambda calculus behaves; both use substitution. First, there’s alpha equivalence, which says we can rename as long as we do so consistently:

lambda x. e = lambda y. e[y/x]
  when y not in fv(e)

Second, there’s beta equivalence, which says that we can substitute actual arguments for formal arguments in a function:

(lambda x. e1) e2 = e1[e2/x]

Note that we get some other principles for free, because = is an equivalence relation:

reflexivity: e = e
symmetry: e1 = e2 iff e2 = e1
transitivity: if e1 = e2 and e2 = e3 then e1 = e3
congruence (application): if e1 = e1' and e2 = e2' then e1 e2 = e1' e2'
congruence (lambda): if e = e' then lambda x. e = lambda x. e'

Using these two rules, we can reduce some lambda expressions. Try to write out the whole derivation of these equalities yourself, subscripting equality with alpha or beta to indicate when you use alpha or beta equivalence, respectively. (Just like in algebra, we don’t need to make a big deal of it when we use properties like reflexivity or congruence.)

(lambda x. x) (lambda y. y) = lambda y. y
(lambda x. z) (lambda y. y) = z
lambda x y. x = lambda a b. a
(lambda x y. x) (lambda z. z) (lambda c. d) = lambda z. z