Alonzo Church lived from 1903 to 1995. In the 1930s he introduced the lambda calculus as a way for mathematics to reason about functions. Later, in the 1960s, computer scientists rediscovered the concept and found it to be a marvelous tool for understanding the nature of computation.
There are three kinds of expression in the lambda calculus:
Accordingly, we shall, for the rest of the lab, abandon the standard lambda calculus notation with the . and use standard Scheme notation.
I could, at this stage, ask you to write a syntax checker and parser for the language "lambda calculus". Instead, I'll give you my code:
(define var? (lambda (x) (symbol? x))) (define lambda? (lambda (x) (and (eq? (car x) 'lambda) ; begins with lambda (var? (var-of x)) ; lists one parameter (null? (cdr (cadr x))) ; and only one (exp? (body-of x)) ; body is a lambda calculus expression (null? (cdddr x)) ; only one body ))) (define var-of caadr) (define body-of caddr) (define app? (lambda (x) (and (exp? (operator-of x)) ; first is a lambda calculus expression (exp? (operand-of x)) ; so is second (null? (cddr x)) ; there is no third or subsequent ))) (define operator-of car) (define operand-of cadr) (define exp? (lambda (x) (or (var? x) (lambda? x) (app? x))))
There are two kinds of variables: Some are free and some are bound In an abstraction lambda (x) body lambda is said to bind occurrences of x within its body. For example, in (lambda (x) ((y x) x)) the lambda binds both occurrences of x. In this example the y is a free variable.
The lambda operator does not bind every occurrence of its variable because "shadowing" can occur. A variable is bound by its nearest enclosing lambda. In (lambda (x) ((y x) (lambda (x) (x y)))) the x in (y x) is bound by the outermost lambda, whereas the x in (x y) is bound by the inner lambda. In this example, both occurrences of y are free.
Here is a recursive definition of what it means to be a free variable in a lambda calculus expression:
> (occurs-free 'y '(lambda (x) (y x))) #t > (occurs-free 'x '(lambda (x) (y x))) #f > (occurs-free 'x '(x (lambda (y) x))) #t > (occurs-free 'y '(x (lambda (y) x))) #f >
Similarly we can define what we mean by a variable occurring bound in a lambda calculus expression.
> (occurs-bound 'x 'x) #f > (occurs-bound 'x '(x (lambda (x) y))) #f > (occurs-bound 'x '(x (lambda (x) x))) #t > (occurs-free 'x '(x (lambda (x) x))) #t >Save all your code, including any code of mine that you borrowed, in a file called ex1.ss.
Note how it's possible for the same variable to both occur free and occur bound in the same lambda calculus expression.
Now we know what the expressions of the lambda calculus are, you are probably eager to know what you can do with them. There are three reduction rules that can be applied to lambda calculus expressions:
I hope you recognize that beta-reduction is essentially what your Scheme interpreter does! It is interesting to observe that the lambda calculus with these three reduction rules gives a powerful and complete model of what function definition and application can do in mathematics. beta-reduction suffices for most programming computations. We need to be careful of eta-conversion when we start to look at lambda calculus in a programming context. It is not always true that f can safely be substituted for (lambda (x) (f x)).
You may complain that so far lambda calculus is pretty useless because there is no concept of actual data! Oh don't be so fussy! There are many ways to add the natural numbers to the lambda calculus. One way is via Church numerals which would have:
(lambda (f) (lambda (x) x)) represent 0 (lambda (f) (lambda (x) (f x))) represent 1 (lambda (f) (lambda (x) (f (f x)))) represent 2and so on. In general n is represented by a function that composes its argument with itself n times.