[plt-scheme] binding construct scoping issue
I've become a bit befuddled over binding constructs.
In particular, I was under the impression that a normal let expression
was not able to define mutually recursive procedures. The r6rs and the
plt-reference imply this through their explanation of letrec. Yet,
(let ((even? (lambda (n)
(if (= n 0)
#t
(odd? (- n 1)))))
(odd? (lambda (n)
(if (= n 0)
#f
(even? (- n 1))))))
(even 20))
=> #t
works just fine using both mzscheme and ypsilon.
TSPL3 describes let as a syntactic extension, expanding into an
application of a lambda expression. Therefore, I assumed that the
previous expression would be expanded into the form:
((lambda (even? odd?)
(even? 20)) (lambda (n)
(if (= n 0)
#t
(odd? (- n 1))))
(lambda (n)
(if (= n 0)
#f
(even? (- n 1)))))
=> #t
which works as well. Why does this work? Both lambda arguments are
defined outside of the body of the calling lambda. Therefore, the
variables within them should be resolved outside. Isn't this dynamic
scoping? For example, the following expressions work as expected:
((lambda (x f) (f)) 1 (lambda () x))
((lambda (g) (define x 1) (g)) (lambda () x))
which both result in errors; x is unidentified.
So, exactly what does let expand to?
Another difficulty I'm having, concerning the same topic, but focused
more on the algorithm, involves defining the same mutually recursive
functions, only using named-let instead:
(let even? ([n 20] [odd? (lambda (n)
(and (not (= n 0))
(even? (- n 1))))])
(or (= n 0)
(odd? (- n 1))))
which works as well, although by looking at its presumed expansion:
((lambda (n odd?)
(define even?
(lambda (x f)
(or (= x 0)
(f (- x 1)))))
(even? n odd?)) 20 (lambda (n)
(and (not (= n 0))
(even? (- n 1)))))
appears it shouldn't, since again, the lambda is passed from outside.
In this case, can anyone tell me why this works given that the even?
procedure called inside of the lambda argument only takes one
argument (even? (- n 1)), while inside it is defined to take two
arguments?