It's not completely equivalent. define
within a procedure body is more like a letrec
so you might get surprised you cannot use the value bound in a define
before all of them are finished and the body of the procedure is to be executed. Imagine you want to do do x + y * z:
(define (test x y z)
(let ((ytimesz (* y z)))
(let ((sum (+ x ytimesz)))
(dosomething sum))))
The reason you have a nested let here is because ytimesz
can't be accessed in the same let as it is made. We have another special form for that let*
(define (test x y z)
(let* ((ytimesz (* y z)) (sum (+ x ytimesz)))
(dosomething sum)))
letrec
and letrec*
are similar but allows for recursion so in a lambda you can call one of the other bound member or itself. Now, depending on the version of Scheme you are using, you will get one of these when writing:
(define (test x y z)
(define ytimesz (* y z))
(define answer (+ x ytimesz)) ;might work, might not
(dosomething answer))
In #!R7RS
, #!R6RS
and #!Racket
it's perfectly ok since this are defined as letrec*
.
In #!R5RS
, however, it won't work at all. The rewrite is done as letrec
and it initializes all variables (ytimesz
and answer
) to an undefined value, then assignes the evaluation of the expressions to temporary variables before set!
-ing the variables to the values of the temporary values to make sure all use of any of them ends up as undefined values and some even signal errors (Racket does in R5RS-mode. For lambda-expressions where the bindings in the body are evaluated at call time, this is no problem and it's for these letrec
and internal define
is originally meant for.
I use define
to store simple values and procedures. The second I think I need to use a precalculated value I might rewrite the whole thing to a let*
or combine define
and a simple let
.