Lexical and Dynamic Scope
What does "scope" mean, anyway? Why haven't we had to deal with it until now?
Let's think back to what happens when you call a procedure:
- Evaluate the arguments.
- Create a new frame and attach it to the procedure's right-bubble environment.
- Evaluate the procedure body in that new environment.
A very common mistake is to create a new frame and attach it to the current environment. Scheme doesn't work that way, but it turns out other languages do! (Like Logo.)
In other words, we have a choice:
- (Lexical) Attach a new frame to the procedure's right-bubble environment.
- (Dynamic) Attach a new frame to the current environment.
How are you going to remember this? Well, normally (in Scheme), you can tell what variables a procedure has access to just by looking at where it's defined. "What variables it can access" is the same as "what environment it's in"! So in lexical scope, we can see environments in the text (code) of the program.
As for dynamic scope, you can't tell what the parent environment will be until the program runs. And "dynamic" means "moving" or "active"!
Thinking about what things mean...what we've been calling the "right-bubble environment" is actually "the environment this procedure is from", i.e. the one it was created in. That's important for things like make-adder
, where we want to remember the value we're adding, not get a new value every time. So lexical scope means extending the environment the procedure came from.
A good way to check what environment you're in is to look at the code and say "what would the value of x be here?". Remember, lexical scope is what we've been working with all semester; it behaves exactly how you're used to.
-
Can you write a Scheme procedure that will tell you if you're in lexical or dynamic scope?
Here's my answer:
> (define test (let ((scope 'lexical)) (lambda () scope))) > (let ((scope 'dynamic)) (test))
In normal Scheme, that creates this environment:
The blue frame extends the environment created for the first
let
, as you'd expect. That's lexical scope.In a dynamically-scoped Scheme, you'd get this one:
Notice that this time, the blue frame extends the second
let
's frame, the one where we are when we calltest
.If you feel like taking an interesting leap, notice that a procedure's right bubble is now useless, since we don't need it for procedure calls. We don't need to save it at all in dynamic scope!
This does explain why Logo doesn't let you define procedures inside other procedures, though. It doesn't do anything!
Flat Scope
This is an "extra" topic; you're not responsible for it, but it's interesting!
If you think about, there's actually another natural choice for what environment you extend when you call a procedure:
- (Lexical) Attach a new frame to the procedure's right-bubble environment.
- (Dynamic) Attach a new frame to the current environment.
- (Flat) Attach a new frame to the global environment.
Flat scope isn't very powerful, but it's very easy to understand: either a variable's defined in the current frame, in the global frame, or not at all. It's what's used in languages like C (which you'll use in 61C), because it's easy.
But this is not exactly true anymore! Both C++0x "lambdas" and Apple's "blocks" are implementations of lambda / closures / higher-order procedures in C, and they're already supported in some of the most popular compilers.
Back to Jordy's home page | Course home page