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) ; define a local procedure double
(+ x x))))
(double (double x)))) ; nested calls to the local procedure
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 defines--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 lets. 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.)