Go to the first, previous, next, last section, table of contents.

Multiple defines are like a letrec

Now that you understand letrec, we can explain what define really does.

Notice that when you define top-level variables and procedures, the procedures you create can refer to other variables in the same top-level environment.

It is as though all of the top-level bindings were created by a single big letrec, so that the initial value expressions create procedures that can "see" each others' name bindings. Expressions that aren't definitions make up the "body" of this imaginary letrec.

Recall that a procedure-defining define is equivalent to a variable-defining define with a lambda expression to compute its initial value.

The following top-level forms

...

(define (foo)
   (... (bar) ...))

(define (bar)
   (... (baz) ...))

(define (baz)
   (... (quux) ...))
...
(foo)
...

are therefore equivalent to

...

(define foo
        (lambda ()
           (... (bar) ...)))

(define bar
        (lambda ()
           (... (baz) ...)))

(define baz
        (lambda ()
           (... (foo) ...)))
...
(foo)
...

When we view top-level defines as being implicitly like parts of a letrec, the program takes the equivalent form

(letrec (... 

         (foo (lambda ()
                 (... (bar) ...)))
         (bar (lambda ()
                 (... (baz) ...)))
         (baz (lambda ()
                 (... (foo) ...)))
         ...)
 ...
 (foo)
 ...)

(Actually, things are scoped like this, but the initial value expressions of defines and the non-definition expressions are evaluated in the order they occurred in the source program. For top-level expressions, you can depend on Scheme executing the executable parts of definitions in the order written.)

Local defines work pretty this way, too. A Scheme interpreter or compiler recognizes any defines that occur at the beginning of a body as being parts of an implicit letrec; the subsequent expressions in the same body are treated as the body of the implicit letrec.

So the following procedure

(define (my-proc)
   (define (local-proc-1)
      ...)
   (define (local-proc-2)
      ...)
   (local-proc-1)
   (local-proc-1))

is equivalent to

(define (my-proc)
   (letrec ((local-proc-1 (lambda () ...))
            (local-proc-2 (lambda () ...)))
      (local-proc-1)
      (local-proc-2)))

If we "desugar" the outer define, too, we get

(define my-proc
        (lambda ()
           (letrec ((local-proc-1 (lambda () ...))
                    (local-proc-2 (lambda () ...)))
             (local-proc-1)
             (local-proc-2)))

Go to the first, previous, next, last section, table of contents.