Scheme lets you define local procedures, scoped inside other procedures or blocks with local variables. This lets you "hide" procedures that only make sense in a certain context, so that they can only be called in that context.
You can define local procedures using let
and lambda
,
like this:
(define (quadruple x) (let ((double (lambda (x) (+ x x)))) (double (double x))))
Here we've defined a procedure named quadruple
, with a local
variable named double
; its value is a procedure that will
double its argument value, created with lambda
.
Notice that when we call double
from inside the procedure
quadruple
, we call it by the name double
, which is
really the name of a local variable. That's okay, because there's no
difference between variable names and procedure names--a call
to a named procedure is always a lookup of a variable
value followed by a call to the procedure it points to.
Also notice that the inner procedure's argument variable x
shadows the outer procedure's argument variable x
. Inside
the body of double
, it refers to double
's argument,
but outside it doesn't. (The code might be easier to read if
we chose different names for the two procedures' arguments, but
this is just for illustration.)
As with a top-level definition, we can write a local definition
using define
instead of let
. For example, we could
have written the above procedure as:
(define (quadruple x) (define (double (x) (+ x x)))) (double (double x))))
A local define
acts a lot like let
with lambda
.
(Actually, it's exactly like a letrec
with lambda
, but
we haven't discussed letrec
yet; we will later.)
There's a restriction on internal define
s--they must be
at the beginning of the procedure body (or the beginning of another
body, like a let
body, before the normal executable
expressions in the body.
Local procedure definitions follow the normal lexical scope rule,
like nested let
s. For example, in the above example, the
formal argument x
of double
is local to
the body of double
---it's a different variable x
than
the argument x
of quadruple
.
(define (quadruple x) (define (double (x) +--------------------------+ | +--------+ | | | (+ x x)|))) | | +--------+ | | (double (double x)) | )) +--------------------------+
Here the inner box is the scope of double
's argument x
,
and the outer one is the scope of the variable double
.
We could have used a different name for the argument to the local procedure, and it wouldn't change the meaning of either procedure:
(define (quadruple x) (define (double (y) ; local defn. of double (+ y y))) ; body of local procedure (double (double x))) ; body of quadruple
On the other hand, since there are no local bindings of +
, +
refers to whatever it refers to in the context where quadruple
is defined. Assuming that quadruple
is a top-level procedure,
not a local procedure in some other scope, +
refers to the top-level
binding of +
. (Remember that a procedure name is really just a
variable name, so the scope rules for variables apply to procedure names
too.)