I'm working my way through SICP, and it gives the following definition for zero
for Church Numerals:
(define zero (lambda (f) (lambda (x) x)))
I have a few questions about that:
Why the complicated syntax? It seems to be quite readable by just having the following instead:
(define (zero f) (lambda (x) x))
where we can see it's a function called
zero
that takes one (unused) argumentf
and returns a function-of-one-parameter that will return its parameter. It almost seems like the definition is just intended to be as non-straightforward as possible.What is the
x
there for? For example doing something like:((zero square) 100)
returns
100
. Isx
just the default value returned?
There is no
x
in(lambda (x) x)
. None.The
x
in(lambda (x) x)
is bound. It could be named by any name whatever. We can not talk aboutx
in(lambda (x) x)
any more than we could talk abouty
in(lambda (y) y)
.There is no
y
in(lambda (y) y)
to speak of. It is just a placeholder, an arbitrary name whose sole purpose in the body is to be the same as in the binder. Same, without regard for which specific name is used there as long as it is used twice -- first time in the binder, and the other time in the body.And in fact there is this whole 'nother notation for lambda terms, called De Bruijn notation, where the same whole thing is written
(lambda 1)
. With1
meaning, "I refer to the argument which the binder 1 step above me receives".So
x
is unimportant. What's important is(lambda (x) x)
which denotes a function which returns its argument as is. The so called "identity" function.But even this is not important here. The Church encoding of a number is really a binary function, a function expecting two arguments -- the
f
and thez
. The "successor step" unary functionf
and the "zero" "value"z
, whatever that might be, as long as the two go together. Make sense together. Work together.So how come we see two unary functions there when it is really one binary function in play?
That is the important bit. It is known as currying.
In lambda calculus all functions are unary. And to represent a binary function an unary function is used, such that when given its (first) argument it returns another unary function, which, when given its (now, second) argument, performs whatever thing our intended binary function ought to perform, using those two arguments, the first and the second.
This is all very very simple if we just write it in combinatory (equational) notation instead of the lambda notation:
where every juxtaposition denotes an application, and all applications associate on the left, so we imagine the above being a shortcut notation for
but it's the same thing. The differences in notation are not important.
And of course there is no
one
inlambda one. (lambda f. (lambda z. f (one f z) ))
. It is bound. It could just be named, I dunno,number
:meaning,
(succ number)
is such a number, which, given thef
and thez
, does with them one moref
step compared to whatnumber
would do.And so,
((zero square) 100)
means, use the numberzero
with the successor stepsquare
and the zero value of100
, and havezero
perform its number of successor steps for us -- that is to say, 0 steps -- starting from the zero value. Thus returning it unchanged.Another possible use is
((zero (lambda (x) 0)) 1)
, or in generalwhich is just another way of writing
Hopefully you can see what
foo
is, easily. And also how to read aloud thist
and thisf
. (Probably the originalf
would be better nameds
, for "successor", or something like that).