So I know that in Scheme define is for dynamic scoping and let for static scoping, yet the following thing confuses me:
If I have
(let ((x 0))
(define f (lambda () x))
(display (f))
(let ((x 1))
(display (f))
)
)
It will display 00. So far so good. However, if I add an extra define for x like so:
(let ((x 0))
(define f (lambda () x))
(display (f))
(define x 4)
(let ((x 1))
(display (f))
)
)
It will display undefined4. Why is this? Why does defining x after evaluating f affect the returned value of (f)? And why is the return value "undefined"?
It is also worth mentioning that binding f with letrec instead of define will also work:
(let ((x 0))
(letrec ((f (lambda () x)))
(display (f))
(define x 4)
(let ((x 1))
(display (f))
)
)
)
Returns 00.
Note: I have used DrRacket with the languge set on "Pretty Big"
In Scheme, you can use local variables pretty much the way you do in most languages. When you enter a let expression, the let variables will be bound and initialized with values. When you exit the let expression, those bindings will disappear.
Local Binding: let, let*, letrec, ... in The Racket Reference also documents let. A let form binds a set of identifiers, each to the result of some expression, for use in the let body: (let ([id expr] ...) body ...+)
For the purpose of their discussion, they define four (slightly different) kinds of scoping rules: trivial: no free variables allowed. static: a free variable takes its value from a set of global variables. lexical: a free variable takes the value of the binding that was in effect when the function was defined.
In a let expression, the initial values are computed before any of the variables become bound; in a let* expression, the bindings and evaluations are performed sequentially; while in a letrec expression, all the bindings are in effect while their initial values are being computed, thus allowing mutually recursive ...
The issue you're experiencing in the second case is that (define x 42)
makes x
a variable for the entire scope in which it's defined. Now, although the variable is defined for the entire scope, its value is undefined until the actual (define x 42)
line.
(let ()
;; up here, `x` is defined but has an undefined value
;; ...
(define x 42)
;; down here `x` has the value 42
;; ...
)
It's acting more like this:
(let ([x 'undefined])
;; ... up here, `x` is 'undefined
(set! x 42)
;; ... down here, `x` is 42
)
Your second and third code snippets are not Scheme (none of R5RS, R6RS nor R7RS). The <body>
(of a let
and others) is defined as:
<body> -> <definition>* <sequence>
<sequence> -> <command>* <expression>
<command> -> <expression>
and thus a define
(which is a <definition>
) cannot follow display
(an <expression>
). You are likely getting confusing results because the compiler/interpreter is incorrectly handling the expansion of 'let'.
Here is what a 'good' R6RS compiler does:
> (let ((x 0))
(letrec ((f (lambda () x)))
(display (f))
(define x 4)
(let ((x 1))
(display (f))
)
)
)
Unhandled exception
Condition components:
1. &who: define
2. &message: "a definition was found where an expression was expected"
3. &syntax:
form: (define x 4)
subform: #f
4. &trace: #<syntax (define x 4)>
>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With