Skip to content

Lab 7: Lambda calculus

This lab focuses on lambda calculus. First, we focus on the syntax of λ-expressions. Second, we focus on its semantics, i.e., the computation specified by λ-expressions whose one step is performed by the β-reduction (and α-conversion if necessary). To help you to play with these notions and concepts, I implemented in Racket an interpreter of λ-calculus transforming a given λ-expression into its normal form (if it exists). It follows the normal order evaluation strategy. In addition, I implemented a few helpers functions to help you inspect λ-expressions. You can download the interpreter: lambda-calculus.rkt

racket
#lang racket
(require "lambda-calculus.rkt")

λ-expressions are represented in the interpreter as S-expressions. It does not allow to use of any conventions regarding parenthesis. So you need to place them all explicitly. For instance, the λ-expression λxy.xy(λab.b) has to be represented as follows:

λ-expressions are represented in the interpreter as S-expressions. It does not allow to use of any conventions regarding parenthesis. So you need to place them all explicitly. For instance, the λ-expression λxy.xy(λab.b) has to be represented as follows:

So we had to add the outermost parenthesis, expand the shortcuts λxy. to λx.(λy., and put the parenthesis determining the order of the applications, i.e., as the application is left-associative, xy(λab.b) is in fact ((xy)(λab.b)). The symbol λ can be entered in DrRacket by pressing Ctrl+\. Instead of the dot symbol, the colon symbol is used.

So we had to add the outermost parenthesis, expand the shortcuts λxy. to λx.(λy., and put the parenthesis determining the order of the applications, i.e., as the application is left-associative, xy(λab.b) is in fact ((xy)(λab.b)). The symbol λ can be entered in DrRacket by pressing Ctrl+\. Instead of the dot symbol, the colon symbol is used.

The module lambda-calculus.rkt provides the following functions:

FunctionDescription
(draw-expr expr)draws the syntax tree the given λ-expression expr; redexes are colored in red
(substitute expr var val)substitutes val for the free occurrences of var in expr
(reduce expr)reduces expr by a one-step β-reduction in the normal order
(eval expr [info 'quiet])finds the normal form of expr; if info is set to 'verbose, displays all the steps; if info is 'tree, then all the steps are drawn as trees

Exercise 1

Draw the syntax tree of the λ-expression (λx.y(xx))(λy.y(xx)) and determine which variable occurrences are free and which are bound.

racket
'((λ x : (y (x x))) (λ y : (y (x x))))

Then evaluate the following function call:

(draw-expr '((λ x : (y (x x))) (λ y : (y (x x)))))

Displays the tree below: ex1

An occurrence of a variable v is bound if it is in the syntax tree below the node λv and is free otherwise. So, for our expression, the occurrences of x in the left branch are bound, and they are free in the right branch. The occurrence of y in the left branch is free and bound in the right branch.

An occurrence of a variable v is bound if it is in the syntax tree below the node λv and is free otherwise. So, for our expression, the occurrences of x in the left branch are bound, and they are free in the right branch. The occurrence of y in the left branch is free and bound in the right branch.

Exercise 2

Draw the syntax tree of the λ-expression λ and determine which variable occurrences are free and which are bound.

Try first to draw the tree on paper. Then compare your result with the result returned by the function draw-expr. The λ-expression is represented in Racket as follows:

Exercise 3

Find all redexes in (λx.xy)z((λu.u)((λv.v)z)). Which one is the leftmost outermost redex, and which is the leftmost innermost redex? Reduce the leftmost outermost redex.

Find all redexes in (λx.xy)z((λu.u)((λv.v)z)). Which one is the leftmost outermost redex, and which is the leftmost innermost redex? Reduce the leftmost outermost redex.

Exercise 4

Recall that multiplication of two numbers is computed by

Mλabc.a(bc).Mλabc.a(bc).

Find the normal form of M01 following the normal order reduction strategy, i.e., compute 01, which should result in 0. The numbers 0,1 are abbreviations for λ-expressions λsz.z and λsz.sz respectively.

Hint

Once you do it on paper, check your result in Racket. You can use Racket definitions and semiquoting to make your λ-expression more readable.

Exercise 5

Recall that a pair (a,b) is represented in λ-calculus as

(a,b)λz.zab.

The projections into the first and second components can be obtained by applying (a,b) to Boolean values Tλab.a and Fλab.b. Thus

(a,b)Fβb.(a,b)Fβb.

We can even define a cons function by CONSλabz.zab. In Racket, you can define all these constructions as follows (the final two calls check that it behaves as expected):

Write a λ-expression swapping components of a given pair p.

Write a λ-expression swapping components of a given pair p.

Hint

The desired λ-expression should take a pair p and return another pair with swapped components. So the expression should start with λpz.z?? where the question marks are the components of the returned pair.

Solution

λpz.z(pF)(pT)

racket
(eval `(,SWAP ((,CONS a) b))) => '(λ z : ((z b) a))
Solution
racket
(define SWAP `(λ p : (λ z : ((z (p ,F)) (p ,T)))))

Exercise 6

Since we can create pairs, we can create lists as in Racket. We represent the empty list by the false value F. Now we can create a list '(a b) by

Since we can create pairs, we can create lists as in Racket. We represent the empty list by the false value F. Now we can create a list '(a b) by

Write a λ-expression NULL? testing if a list is empty, i.e., it returns T if it is empty and F otherwise.

Write a λ-expression NULL? testing if a list is empty, i.e., it returns T if it is empty and F otherwise.

Hint

A list is either a pair or F if it is empty. Let denote it by p. Recall the definition of the zero test from the lecture

Zλx.xF¬F,

where ¬λx.xFT. We need something similar for the list p. So our desired λ-expression should look like λp.pe1e2 where e1,e2 have to be filled by suitable λ-expressions serving as arguments for p. If p is empty (i.e., pF), then p is just a projection into the second argument. Thus e2 should be T, i.e., we have λp.pe1T. If we substitute for p a pair (i.e. pλz.zab), we obtain (λz.zab)e1T. Thus e1 is going to be substituted for z, and consequently, it will be applied to a and b, i.e., we would end up with e1abT. Since the result in this case should be F, we need the result of e1ab to be ¬ because ¬FβT.

Solution

λp.p(λab.¬)T

Code
racket
(define neg `(λ x : ((x ,F) ,T)))
(define NULL? `(λ p : ((p (λ a : (λ b : ,neg))) ,T)))

(eval `(,NULL? ,F)) ; => T
(eval `(,NULL? ,lst)) ; => F

Exercise 7

Define a λ-expression computing the length of a list.

Define a λ-expression computing the length of a list.

Hint

Follow the approach from the lecture where we defined a function

Rλrn.Zn0(nS(r(Pn)))

which we turned into a recursive one by Y-combinator (i.e., YR).

Recall:

Yλy.(λx.y(xx))(λx.y(xx)).

You also need the successor function

Sλwyx.y(wyx)

for adding 1. The computation of the desired λ-expression can be expressed in Racket as follows:

Modify the λ-expression R by replacing Z withNULL? from the previous exercise. Adding 1 can be done by applying the successor function S, and the predecessor function P must be replaced by the expression returning the second component.

Modify the λ-expression R by replacing Z withNULL? from the previous exercise. Adding 1 can be done by applying the successor function S, and the predecessor function P must be replaced by the expression returning the second component.

Solution

LENλrp.NULL?p0(S(r(pF)))

Code
racket
(define S '(λ w : (λ y : (λ x : (y ((w y) x))))))
(define Y '(λ y : ((λ x : (y (x x))) (λ x : (y (x x))))))

(define LEN
  `(λ r :
     (λ lst : (
               ((,NULL? lst) ,zero)
               (,S (r (lst ,F)))))))

(eval `((,Y ,LEN) ,F)) ; => 0
(eval `((,Y ,LEN) ((,CONS a) ,F))) ; => 1
(eval `((,Y ,LEN) ((,CONS a) ((,CONS b) ,F)))) ; => 2