In case representing pairs as procedures wasn't mind-boggling enough, consider that, in a language that can manipulate procedures, we can get by without numbers (at least insofar as nonnegative integers are concerned) by implementing 0 and the operation of adding 1 as
(define zero (lambda (f) (lambda (x) x)))
(define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
This representation is known as Church numerals, after its inventor, Alonzo Church, the logician who invented the λ calculus.
Define one
and two
directly (not in terms of zero
and add-1
). (Hint: Use substitution to evaluate (add-1 zero)
). Give a direct definition of the addition procedure +
(not in terms of repeated application of add-1
).
Let's take the hint and run with it...
(add-1 zero)
= (add-1 (lambda (f) (lambda (x) x)))
= (lambda (f) (lambda (x) (f (((lambda (f) (lambda (x) x)) f) x))))
= (lambda (f) (lambda (x) (f ((lambda (x) x) x))))
= (lambda (f) (lambda (x) (f x)))
That give us a definition for
one
:
(define one
(lambda (f) (lambda (x) (f x))))
And we can do the same to figure out
two
:
(add-1 one)
= (add-1 (lambda (f) (lambda (x) (f x))))
= (lambda (f) (lambda(x) (f (((lambda (f) (lambda (x) (f x))) f) x))))
= (lambda (f) (lambda(x) (f (((lambda (x) (f x)) x))))
= (lambda (f) (lambda(x) (f (f x))))
So
two
is:
(define two
(lambda (f) (lambda (x) (f (f x)))))
Looking at the pattern we can see that the Church numeral representation of the positive integer
n is
fn(
x) (i.e. (
f ο
f ο…ο
f ο
f)(
x), with
n occurrences of
f). Also, to assist with the remainder of the exercise, we should note that:
zero
produces a single-parameter lambda expression that ignores its parameter and returns a single-parameter lambda expression that returns its parameter. So we can convert zero
to its integer equivalent value using the expression ((zero f) 0)
, where f
is any value.
one
produces a single-parameter, f
, lambda expression that returns a new single-parameter lambda expression that returns the results of applying f
to its parameter. So, assuming we pass 0 as the parameter to the second lambda expression as with zero
, then we can convert one
to its integer equivalent value using the expression ((one inc) 0)
, where inc
is the increment procedure defined earlier.
two
produces a single-parameter, f
, lambda expression that returns a new single-parameter lambda expression that returns the results of applying f
to the results of applying f
to its parameter. So similar to one
, we can convert two
to its integer equivalent value using the expression ((two inc) 0)
.
We can generalize this to saying that for any Church numeral,
n
, we can convert it to its integer equivalent value using the expression
((n inc) 0)
. Here's a procedure that encapsulates this logic which we can use to allow easy testing of our work with Church numerals:
(define (unchurch n)
((n inc) 0))
For example:
> (unchurch zero)
0
> (unchurch one)
1
> (unchurch two)
2
Now onto defining
+
for Church numerals. We know that we want this to produce a valid Church numeral. I.e. a single-parameter lambda expression whose parameter,
f
, is a procedure and that returns a single-parameter lambda expression. We also know that, given two Church numerals representing the nonnegative integers
a and
b, we want the evaluation of
(((+ a b) f) x)
to apply
f
repeatedly, starting with
x
as the initial input, for a total of
a+
b times. How can we do this? Well evaluating
((a f) x)
will apply
f
a total of
a
times to whatever is passed to it as
x
, and
((b f) x)
will apply
f
a total of
b
times to whatever is passed to it as
x
. So we can produce a composite function that evaluates
((b f) x)
and then uses that as the value of
x
for evaluating
((a f) x)
(or vice versa). Here's my definition
(define (+ a b)
(lambda (f) (lambda (x) ((a f) ((b f) x)))))
And to check it works:
> (unchurch (+ one two))
3
> (unchurch (+ (+ one two) two))
5
Of course we can go much further with Church numerals - there's a
whole raft of definitions for various computations. Just for fun I also defined
*
for Church numerals:
(define (* a b)
(lambda (f) (lambda (x) ((a (b f)) x))))
And here it is in action:
> (unchurch (* (+ one two) two))
6
> (unchurch (* (+ two two) (+ one two)))
12
> (unchurch (* (* two two) (* one two)))
8