Lambda

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 10

Foundations of Computer Science ENGR 3520 Fall 2013

Lecture Notes Thursday, Nov 21, 2013

-calculus
-terms. A -term is either: A variable x, y , z , . . . x.M MN (M ) Examples: x Conventions: Application associates to the left, so that M N P should be understood as (M N ) P . The body of a function x.M goes as far to the right as possible. Thus, x.y.x should be understood as x.(y.x). The scope of x in x.M is all of M . A variable x is free in a -term if some use of x does not appear in the scope of a x. Examples: y is free in x.y ; x is free in y.(x (x.x)); z is not free in z.(x.z ) Substitution. Substitution: M [x N ] x[ x N ] = N y [x N ] = y (if x = y ) (where x is a variable and M a -term) (where M, N are -terms) (where M is a -term) x.x y.(x.x) x.(x (y.y ))

(M1 M2 )[x N ] = M1 [x N ] M2 [x N ] (x.M )[x N ] = x.M (y.M )[x N ] = y.(M [x N ]) (if y is not free in N )

We can always rename bound variables to avoid conict: 1

x.M = y.(M [x y ]) if y is not free in M . (There is a question what happens when the substitution rules do not apply. For instance, (x.y )[y x] is not dened, because x, the variable parameter in x.y , is free in x, the term being substituted for y . One possibility is to return an error. Another possibility is to apply renaming to the problematic variable parameter to get (z.y )[y x] which by the substitution rules above gives you z.x. The latter approach is often called capture-avoiding substitution.) Reduction Rules. Reduction (or evaluation): M N (x.M ) N M [x N ] M P N P P M P N x.M x.N (if M N ) (if M N ) (if M N )

A term of the form (x.M ) N is called a redex. It is the one place where a reduction does any work. Examples:

(x.x) (y.y ) x[x y.y ] = y.y

((x.(y.x)) z1 ) z2 = =

(y.x)[x z1 ] z2 (y.z1 ) z2 z1 [y z2 ] z1

((x.(y.y )) (z.z )) (x.(y.x)) = =

(y.y )[x z.z ] (x.(y.x)) (y.y ) (x.(y.x)) y [y x.(y.x)] x.(y.x)

The -term ((x.(y.x)) z1 ) z2 could be written more simply as (x.y.x) z1 z2 using the conventions above. Similarly, ((x.(y.y )) (z.z )) (x.(y.x)) could be written (x.y.y ) (z.z ) (x.y.x) I will use the conventions fully from now on. I will generally expand out substitutions immediately in reductions, skipping the intermediate step of showing what the substitution looks like. A -term is in normal form if there is no applicable reduction. Not every -term eventually reduces to a normal form:

(x.x x) (x.x x) (x.x x) (x. x) (x.x x) (x. x) . . . There can be more than one redex in a -term, meaning that there may be more than one applicable reduction. For instance, ((x.x) (y.x)) ((x.y.x) z1 z2 ). A property of the calculus is that all the ways to reduce a term to a normal form yield the same normal form (up to renaming of bound variables). This is called the Church-Rosser property. It says that the order in which we perform reductions to reach a normal form is not important. Encoding Booleans. Even though the lambda-calculus only has variables and functions, we can encode traditional data types within in. Boolean values encoding (` a la Church):

true = x.y.x false = x.y.y In what sense are these encodings of Boolean values? Booleans are useful because they allow you to select one branch or the other of a conditional expression. The trick is that when B reduces to either true or false, then B M N reduces either to M or to N , respectively: 3

If B true, then B M N = while if B false, then B M N = We can also dene an explicit if expression: if = c.x.y.c x y so that B M N could also be written if B M N . We can dene logical operators: and = m.n.m n m or = m.n.m m n not = m.x.y.m y x Thus: and true false = = (m.n.m n m) true false (n.true n true) false true false true (x.y.x) false true (y.false) true false false M N (x.y.y ) M N (y.y ) N N true M N (x.y.x) M N (y.M ) N M

not false = = =

(m.x.y.m y x) false (x.y.false y x) (x.y.(u.v.v ) y x) (x.y.(v.v ) x) (x.y.x) true 4

Encoding Natural Numbers. 0 = f.x.x 1 = f.x.f x 2 = f.x.f (f x) 3 = f.x.f (f (f x)) 4 = ... In general, natural number n is encoded as f.x.f n x. Successor operations: succ = n.f.x.(n f ) (f x)

succ 1 = = Other operations:

(n.f.x.(n f ) (f x)) (f.x.f x) f.x.((f.x.f x) f ) (f x) f.x.(x.f x) (f x) f.x.f (f x) 2

plus = m.n.f.x.(m f ) (n f x) times = m.n.f.x.m (n f ) x iszero? = n.n (x.false) true

plus 1 2 = = =

(m.n.f.x.(m f ) (n f x)) 1 2 (n.f.x.(1 f ) (n f x)) 2 f.x.(1 f ) (2 f x) f.x.((f.x.f x) f ) ((f.x.f (f x)) f x) f.x.(x.f x) ((f.x.f (f x)) f x) f.x.f ((f.x.f (f x)) f x) f.x.f ((x.f (f x)) x) f.x.f (f (f x)) 3

times 2 3 = = =

(m.n.f.x.m (n f ) x) 2 3 (n.f.x.2 (n f ) x) 3 f.x.2 (3 f ) x f.x.(f.x.f (f x)) ((f.x.f (f (f x))) f ) x f.x.(f.x.f (f x)) (x.f (f (f x))) x f.x.(x.(x.f (f (f x))) ((x.f (f (f x))) x)) x f.x.(x.(x.f (f (f x))) (f (f (f x)))) x f.x.(x.f (f (f (f (f (f x)))))) x f.x.f (f (f (f (f (f x))))) 6

iszero? 0 =

(n.n (x.false) true) (f.x.x) (f.x.x) (x.false) true (x.x) true true

iszero? 2 =

(n.n (x.false) true) (f.x.f (f x)) (f.x.f (f x)) (x.false) true (x.(x.false) ((x.false) x)) true (x.false) ((x.false) true) false

More dicult is dening a predecessor function, taking a nonzero natural number n and returning n 1. There are several ways of dening such a function; here is probably the simplest: pred = n.f.x.n (g.h.h (g f )) (u.x) (u.u)

pred 2 = (n.f.x.n (g.h.h (g f )) (u.x) (u.u)) (f.x.f (f x)) f.x.(f.x.f (f x)) (g.h.h (g f )) (u.x) (u.u) f.x.(x.(g.h.h (g f )) ((g.h.h (g f )) x)) (u.x) (u.u) 6

f.x.(g.h.h (g f )) ((g.h.h (g f )) (u.x)) (u.u) f.x.(g.h.h (g f )) (h.h ((u.x) f )) (u.u) f.x.(g.h.h (g f )) (h.h x) (u.u) f.x.(h.h ((h.h x) f )) (u.u) f.x.(h.h (f x)) (u.u) f.x.(u.u) (f x) f.x.f x 1

Note that pred 0 is just 0: pred 0 = = Encoding Pairs. pair = x.y.z.z x y rst = p.p (x.y.x) second = p.p (x.y.y ) (n.f.x.n (g.h.h (g f )) (u.x) (u.u)) (f.x.x) f.x.(f.x.x) (g.h.h (g f )) (u.x) (u.u) f.x.(x.x) (u.x) (u.u) f.x.(u.x) (u.u) f.x.x 0

rst (pair 1 2) =

(p.p (x.y.x)) ((x.y.z.z x y ) 1 2) (p.p (x.y.x)) ((y.z.z 1 y ) 2) (p.p (x.y.x)) (z.z 1 2) (z.z 1 2) (x.y.x) (x.y.x) 1 2 (y.1) 2 1

second (pair 1 2) = (p.p (x.y.y )) ((x.y.z.z x y ) 1 2) (p.p (x.y.y )) ((y.z.z 1 y ) 2) 7

(p.p (x.y.y )) (z.z 1 2) (z.z 1 2) (x.y.y ) (x.y.y ) 1 2 (y.y ) 2 2

Recursion. With conditionals and basic data types, we are very close to having a Turingcomplete programming language (that is, one that can simulate Turing machines). All that is missing is a way to do iteration: loops. It turns out we can write recursive functions in the -calculus, which gives us loops. Consider factorial. Intuitively, we would like to dene fact by fact = n.(iszero? n) 1 (times n (fact (pred n))) but this is not a valid denition, since the right-hand side refers to the term being dened. It is really an equation, the same way x = 3x is an equation. Consider that equation, x = 3x. Dene F (t) = 3t. Then, a solution of x = 3x is really a xed-point of F , namely, a value t0 for which F (t0 ) = t0 . And F has only one xed-point, namely t0 = 0, which gives us the one solution to x = 3x, namely x = 0. Similarly, if we dene Ffact = f.n.(iszero? n) 1 (times n (f (pred n))) then we see that the denition that were looking for is a xed point of Ffact , namely, a term f such that Ffact f = f . Indeed, if we have such a term, then:

f3= = = = =

Ffact f 3 (f.n.(iszero? n) 1 (times n (f (pred n)))) f 3 (n.(iszero? n) 1 (times n (f (pred n)))) 3 (iszero? 3) 1 (times 3 (f (pred 3))) times 3 (f (pred 3)) times 3 (f 2) times 3 (Ffact f 2) times 3 (times 2 (f 1)) times 3 (times 2 (Ffact f 1)) times 3 (times 2 (times 1 (f 1))) times 3 (times 2 (times 1 (Ffact f 1))) times 3 (times 2 (times 1 1)) 8

(The notation indicates that one or more reductions have taken place.) Thus, what we need is a way to nd xed points in the -calculus. The following function does just that: Y = f.(x.f (x x)) (x.f (x x)) Y F gives us a xed point of F . (Technically, Y F reduces in one step to a xed-point of F ):

YF =

(f.(x.f (x x)) (x.f (x x))) F (x.F (x x)) (x.F (x x)) F ((x.F (x x)) (x.F (x x))) F (F ((x.F (x x)) (x.F (x x)))) ...

So indeed, (x.F (x x)) (x.F (x x)) is a xed point of F . We can use Y to dene our factorial function: fact = Y Ffact and we can check: fact 3 = = = = = = = Y Ffact 3 (f.(x.f (x x)) (x.f (x x))) Ffact 3 (x.Ffact (x x)) (x.Ffact (x x)) 3 Ffact ((x.Ffact (x x)) (x.Ffact (x x))) 3 Ffact fact 3 (f.n.(iszero? n) 1 (times n (f (pred n)))) fact 3 (n.(iszero? n) 1 (times n (fact (pred n)))) 3 (iszero? 3) 1 (times 3 (fact (pred 3))) times 3 (fact (pred 3)) times 3 (fact 2) times 3 (Ffact fact 2) times 3 (times 2 (fact 1)) times 3 (times 2 (Ffact fact 1)) times 3 (times 2 (times 1 (fact 1))) times 3 (times 2 (times 1 (Ffact fact 1))) 9

times 3 (times 2 (times 1 1)) 6

where fact = (x.Ffact (x x)) (x.Ffact (x x)) is the xed point of Ffact , such that fact = Ffact fact .

10

You might also like