Lambda calculus

Martin Sulzmann

Lambda calculus

The lambda calculus represents a minimal core language just consisting of functions.

Functions are first-class (can appear as arguments and as return values). Think of first-class objects!

Lambda calculus: Syntax and semantics

Terms (expressions/programs) in the lambda calculus are defined by the following (abstract) EBNF syntax.

t::=xλx.ttt t \ \ ::= \ \ x \ \ \mid \ \ \lambda x.t \ \ \mid \ \ t \ t

where

The above EBNF is ambiguous. For example, consider expression λx.xx\lambda x.x \ x. Based on the above EBNF rules there are two possible interpretations.

  1. A lambda function with formal parameter xx where xx is applied to itself in the function body.

  2. The identity function λx.x\lambda x.x applied to xx.

The first interpretation can be explained via the following (parse tree) derivation tλx.tλx.ttλx.xtλx.xx t \rightarrow \lambda x.t \rightarrow \lambda x. t \ t \rightarrow \lambda x. x \ t \rightarrow \lambda x.x \ x

whereas for the second derivation we find ttt(λx.t)t(λx.x)t(λx.x)x t \rightarrow t \ t \rightarrow (\lambda x. t) \ t \rightarrow (\lambda x.x) \ t \rightarrow (\lambda x.x) \ x

In the above derivation, parantheses "(..)" are meta-symbols to highlight the difference among the two derivations.

In examples, we therefore may make use of parantheses to avoid ambiguities, for example (λx.x)x(\lambda x.x) \ x.

The operational semantics of the lambda calculus is defined via a single rule, commonly referred to as β\beta-reduction.

(λx.t1)t2[t2/x]t1 (\lambda x. t_1) \ t_2 \rightarrow [t_2/x]t_1

In [t2/x]t1[t_2/x]t_1, we apply the substitution [t2/x][t_2/x]: In the body t1t_1, replace each occurence of the formal parameter xx by the argument t2t_2.

Commonly, we refer to λx.e\lambda x.e as a lambda-abstraction and to λx\lambda x as the lambda-binding.

For example, (λx.(λy.y))z(\lambda x. (\lambda y. y)) \ z reduces to λy.y\lambda y.y.

As it is common in programming languages, we can reuse the same variable names and apply the common rule that each use of a variable name refers to the closest binding site. For example, consider λx.(λx.x)\lambda x. (\lambda x. x) where the innermost occurence of xx refers to the inner lambda-binding.

Via some simply renaming we can always guarantee that variables are distinct. For example, λx.(λx.x)\lambda x. (\lambda x. x) can be renamed to λx.(λy.y)\lambda x. (\lambda y. y).

We generally assume that lambda-bindings always introduce fresh, distinct variables.

Details

Syntactic sugar

Function application is left associative. Hence, the term (λu.u)zx(\lambda u.u) \ z \ x is a short-hand for (((λu.u)z)x)(((\lambda u.u) \ z) \ x).

The body of a lambda abstractions extends to the right as long as possible. Hence, the term λz.(λu.u)zx\lambda z. (\lambda u.u) \ z \ x is a short-hand for λz.(((λu.u)z)x)\lambda z. (((\lambda u.u) \ z) \ x).

Free variables

fv(t)fv(t) computes all free variables in tt, i.e. those variables which are not bound by a lambda abstraction.

The definition by induction over the structure of tt is as follows:

In case of name clashes we must rename bound variables. Consider [y(λx.x)/x]λy.(λx.x)yx[y \ (\lambda x.x)/x] \lambda y.(\lambda x.x) \ y \ x:

  1. Renaming yields: [y(λv.v)/x]λz.(λu.u)zx[y \ (\lambda v.v)/x] \lambda z. (\lambda u.u) \ z \ x
  2. Substitution without name clashes yields: λz.(λu.u)z(y(λv.v))\lambda z. (\lambda u.u) \ z(y \ (\lambda v.v))

Substitution

Examples (where ee refers to a term/expression)

Evaluation strategies

There are two principles ways how to evaluate terms:

Innermost

Call-by value (CBV) = AOR with the exception that we don't evaluate under "lambda" (i.e. inside function bodies)

Outermost

Outermost, leftmost, also called Normal Order Reduction (NOR)

Call-by name (CBN) = NOR with the exception that we don't evaluate under "lambda" (i.e. inside function bodies)

Examples

We consider evaluation of (λf.λx.f(fx))((λx.x)(λx.x))(\lambda f.\lambda x. f \ (f \ x)) ((\lambda x.x) (\lambda x.x)) where \rightarrow denotes an evaluation step

Call-by-name (CBN)

(λf.λx.f(fx))((λx.x)(λx.x))(\lambda f.\lambda x. f \ (f \ x)) ((\lambda x.x) (\lambda x.x))

λx.(λx.x)(λx.x)((λx.x)(λx.x)x)\rightarrow \lambda x.(\lambda x.x)(\lambda x.x)((\lambda x.x)(\lambda x.x)x)

λx.(λx.x)((λx.x)x)\rightarrow \lambda x.(\lambda x.x)((\lambda x.x)x)

λx.(λx.x)x\rightarrow \lambda x.(\lambda x.x)x

λx.x\rightarrow \lambda x.x

Call-by-value (CBV):

(λf.λx.f(fx))((λx.x)(λx.x))(\lambda f.\lambda x. f \ (f \ x)) ((\lambda x.x) (\lambda x.x))

(λf.λx.f(fx))(λx.x)\rightarrow (\lambda f.\lambda x. f \ (f \ x)) (\lambda x.x)

λx.(λx.x)((λx.x)x)\rightarrow \lambda x.(\lambda x.x) ((\lambda x.x) x)

λx.(λx.x)x\rightarrow \lambda x.(\lambda x.x)x

λx.x\rightarrow \lambda x.x

(Non)Termination

There are lambda terms whose evaluation will not terminate:

(λx.(xx))(λx.(xx))(\lambda x. (x \ x)) (\lambda x. (x \ x))

(λx.(xx))(λx.(xx))\rightarrow (\lambda x. (x \ x)) (\lambda x. (x \ x))

(λx.(xx))(λx.(xx))\rightarrow (\lambda x. (x \ x)) (\lambda x. (x \ x))

...\rightarrow ...

Termination under CBN is more likely than termination under CBV.

Let's abbreviate (λx.(xx))(λx.(xx))(\lambda x. (x \ x)) (\lambda x. (x \ x)) by Ω\Omega. Then, the lambda term (λx.λy.x)Ω(\lambda x. \lambda y. x) \Omega terminates under CBN but not under CBV.

Short summary

More details in some upcoming exercises.

Further examples

Consider (λx.xy)(λx.x)(λx.x)(\lambda x. x \ y) \ (\lambda x. x) \ (\lambda x. x).

Function application is left-associative, therefore, the meaning of the above lambda term is ((λx.xy)(λx.x))(λx.x)((\lambda x. x \ y) \ (\lambda x. x)) \ (\lambda x. x).

Let's carry out the evaluation steps.

((λx.xy)(λx.x))(λx.x)((\lambda x. x \ y) \ (\lambda x. x)) \ (\lambda x. x)

=Renaming((λx1.x1y)(λx2.x2))(λx3.x3)=_{Renaming} \ \ ((\lambda x_1. x_1 \ y) \ (\lambda x_2. x_2)) \ (\lambda x_3. x_3)

((λx2.x2)y)(λx3.x3)\rightarrow \ \ ( (\lambda x_2. x_2) \ y) (\lambda x_3. x_3) because (λx1.x1y)(λx2.x2)[(λx2.x2)/x1](x1y)=(λx2.x2)y(\lambda x_1. x_1 \ y) \ (\lambda x_2. x_2) \ \rightarrow \ [(\lambda x_2. x_2) / x_1 ] (x_1 \ y) \ = \ (\lambda x_2. x_2) \ y

y(λx3.x3)\rightarrow \ \ y \ (\lambda x_3. x_3) because (λx2.x2)y[y/x2]x2=y(\lambda x_2. x_2) \ y \ \rightarrow \ [y / x_2] x_2 \ = \ y

On the other hand, the lambda term (λx.xy)((λx.x)(λx.x))(\lambda x. x \ y) \ ((\lambda x. x) \ (\lambda x. x)) evaluates to yy. Briefly, the evaluation steps are

(λx.xy)((λx.x)(λx.x))(\lambda x. x \ y) \ ((\lambda x. x) \ (\lambda x. x))

Renaming(λx1.x1y)((λx2.x2)(λx3.x3))-_{Renaming} \ (\lambda x_1. x_1 \ y) \ ((\lambda x_2. x_2) \ (\lambda x_3. x_3))

(λx1.x1y)(λx3.x3)\rightarrow \ (\lambda x_1. x_1 \ y) \ (\lambda x_3. x_3)

(λx3.x3)y\rightarrow \ (\lambda x_3. x_3) \ y

y\rightarrow \ y

Expressiveness/Church Encoding

How expressive is the lambda calculus? As expressive as a Turing machine?

Yes! But how? The lambda calculus looks rather simple.

Indeed, but as we will show next, we can encode data types (booleans, ...), conditional expressions, recursion.

The idea of the Church encoding (named after Alonzo Church):

Boolean operations

The idea:

The actual encoding:

which written in 'lambda notation' is

ite = λe1.λe2.λe3.e1e2e3\lambda e_1. \lambda e_2. \lambda e_3. e_1 \ e_2 \ e_3

Example: if true then e2e_2 else e3e_3 equals (λx.λy.x)e2e3(\lambda x. \lambda y. x) \ e_2 \ e_3

(λx.λy.x)e2e3(\lambda x. \lambda y. x) \ e_2 \ e_3

e2\rightarrow e_2

In detail:

(λx.λy.x)e2e3(\lambda x. \lambda y. x) \ e_2 \ e_3

= ((λx.λy.x)e2)e3((\lambda x. \lambda y. x) \ e_2) \ e_3

by adding parentheses (recall that function application is left associative)

(λy.e2)e3\rightarrow (\lambda y.e_2) \ e_3

e2\rightarrow e_2

Boolean operations example

We evaluate itetruexyite \ true \ x \ y. The expect result is xx. Let's see!

  1. Replace short-hands by actual definitions.

itetruexy=(λx.λy.λz.xyz)(λx.λy.x)xyite \ true \ x \ y = (\lambda x. \lambda y. \lambda z. x \ y \ z) \ (\lambda x. \lambda y. x) \ x \ y

  1. Rename variables such that all bound variables are distinct

(λx1.λy1.λz1.x1y1z1)(λx2.λy2.x2)xy(\lambda x_1. \lambda y_1. \lambda z_1. x_1 \ y_1 \ z_1) \ (\lambda x_2. \lambda y_2. x_2) \ x \ y

  1. Introduce parantheses

(((λx1.λy1.λz1.(x1y1)z1)(λx2.λy2.x2))x)y(((\lambda x_1. \lambda y_1. \lambda z_1. (x_1 \ y_1) \ z_1) \ (\lambda x_2. \lambda y_2. x_2)) \ x) \ y

  1. Carry out evaluation steps

(((λx1.λy1.λz1.(x1y1)z1)(λx2.λy2.x2))x)y(((\lambda x_1. \lambda y_1. \lambda z_1. (x_1 \ y_1) \ z_1) \ (\lambda x_2. \lambda y_2. x_2)) \ x) \ y

(([(λx2.λy2.x2)/x1](λy1.λz1.(x1y1)z1))x)y\rightarrow (([(\lambda x_2. \lambda y_2. x_2) / x_1] (\lambda y_1. \lambda z_1. (x_1 \ y_1) \ z_1)) \ x) \ y

=(((λy1.λz1.((λx2.λy2.x2)y1)z1))x)y= (((\lambda y_1. \lambda z_1. ((\lambda x_2. \lambda y_2. x_2) \ y_1) \ z_1)) \ x) \ y

Several redexes. We choose the outermost redex

([x/y1](λz1.((λx2.λy2.x2)y1)z1))y\rightarrow ([x / y_1] (\lambda z_1. ((\lambda x_2. \lambda y_2. x_2) \ y_1) \ z_1)) \ y

=(λz1.((λx2.λy2.x2)x)z1)y= (\lambda z_1. ((\lambda x_2. \lambda y_2. x_2) \ x) \ z_1) \ y

[y/z1](((λx2.λy2.x2)x)z1)\rightarrow [y / z_1] (((\lambda x_2. \lambda y_2. x_2) \ x) \ z_1)

=((λx2.λy2.x2)x)y= ((\lambda x_2. \lambda y_2. x_2) \ x) \ y

([x/x2](λy2.x2))y\rightarrow ([x / x_2] (\lambda y_2. x_2)) \ y

=(λy2.x)y= (\lambda y_2. x) \ y

[y/y2]x\rightarrow [y / y_2] x

=x= x

Recursion

What about recursion?

     factorial n = if n == 0 then 1
                   else n * (factorial (n-1))

Idea:

This works!

factorial1factorial \ 1

(def)(YFac)1\rightarrow (def) \ (Y \ Fac) \ 1

(fix)(Fac(YFac))1\rightarrow (fix) \ (Fac \ (Y \ Fac)) \ 1

(def)((λfac.λn.if(n==0)then1elsen*(fac(n1))))(YFac))1\rightarrow (def) \ ((\lambda fac. \lambda n. if \ (n == 0) \ then \ 1 \ else \ n*(fac (n-1)))) (Y Fac)) \ 1

(λn.if(n==0)then1elsen*((YFac)(n1)))1\rightarrow \ (\lambda n. if \ (n==0) \ then \ 1 \ else \ n*((Y \ Fac) \ (n-1))) \ 1

1*((YFac)0)\rightarrow \ 1 * ((Y \ Fac) \ 0)

(fix)1*((Fac(YFac))0)\rightarrow (fix) \ 1 * ((Fac \ (Y \ Fac)) \ 0)

1*((λn.if(n==0)then1elsen*((YFac)(n1)))0)\rightarrow \ 1 * ((\lambda n. if \ (n == 0) \ then \ 1 \ else \ n*((Y \ Fac) \ (n-1))) \ 0)

1*1\rightarrow \ 1 * 1

1\rightarrow \ 1

More encodings (pairs, natural numbers) are possible. See Church encoding.