If you haven’t read my notes on environment diagrams, now is a good time to read those.
With the exception of the global frame, every function’s execution frame has a parent: the frame in which the function was created. When you call a function, the function’s frame first looks for references within its frame. If it doesn’t find one, it goes up a frame and keeps doing this until it hits the global frame. If it still doesn’t find a reference to a specified variable or function, the variable or function doesn’t exist.
This idea is crucial: a frame can only deal with variables in or above it, and a frame is only created upon execution of a function. This is also why a frame doesn’t necessarily become irrelevant upon completion. Check out this demo. Here’s the code, for convenience:
a = 1 def x(b): c = 3 def y(d): e = 5 return b + d + e return y z = x(a + 1) f = 6 z(4)
If you step through this code, you’ll see that the global frame first contains
x. When we try to assign
z, we call
x, creating a new frame. The first thing that happens is that every parameter gets evaluated and written into the new frame. In
x, we write the value of
b into the frame right off the bat. Then we add
y to our frame. Notice that the function description of
[parent=f1]. This is important –
f1 is the frame we created for
x, as it’s now labeled. For the context of 61A, you’ll always label your frames, even if it becomes irrelevant upon completion. Now, we return
x is done executing. Why doesn’t it disappear?
Because there’s a reference within the frame of
x that we could use outside of it. As long as there are references pointing to anything inside that frame, we cannot get rid of it. There is now a
z value in the global frame, and we proceed to create a new
f variable. This is mostly to show that
f1 sticks around.
A new frame is created when we call
z (notice that it’s a higher order function).
Every frame contains variables and functions, and everything in a frame can refer to everything else in that frame. The idea behind having indiividual frames for every function execution is to make sure that a function only has access to what it needs. Giving it extraneous information could unnecessarily slow down the program, for example, so you’d want to find a way to just store the information you need.