490 likes | 641 Views
II. Frames and frame structures Frame – set of bindings generated together in a binding generation event (frame ~ mini environment ) When an event generates frame F , then the new environment is (for some E ) E itself was similarly generated
E N D
II. Frames and frame structures • Frame – set of bindings generated together in a binding generation event (frame ~ minienvironment ) • When an event generates frame F, then the new environment is (for some E) • E itself was similarly generated • In the future, E may become again the current environment • Frames are added/removed in LIFO A stack discipline environment
Environment structure --- past and present frames -- a tree: each path from root is a stack of frames • Activation start(binding generation event) • generation of frame • implemented as push (onto current environment) • Look-up --- search in frame stack, top-to-bottom • Activation exit -- restoration of previous environment • Frame disposed of(automatically/garbage collection) --- pop Let us re-visit previous example environment
Example: (start with empty environment) Activationdirectly evaluatedframe The value 7 is returned, down to to act0 environment
Dynamic scoping Results of resolutions depend on how environments are modified when a activation is entered Option A: new generated bindings are always merged into current environment (at point of entrance) dynamic scoping • Simple, elegant approach • Works well with stack-based implementations of pl’s We discuss implementation, present examples , show that approach is WRONG environment
Activation and frame stacks are in 1-1 correspondence! • Each activation A --- associated frame F(A) generated & removed together • Look-up when A is current (top of activation stack) starts at F(A) (then top of frame stack) then (if needed) goes down Wlog: going down uses the dynamic parent to reach the activation below, then its frame Look-up follows the dynamic parents chain, until a binding is found (or fail announced) environment
Example revisited (let* x =3, f = ….) Act framed-evalexp evaluatedexp The result 7 is returned from #3 to #2 to #1 to #0 Q: What would happen if the body of f contained f? environment
Example: We start from g(3), after the bindings of the let* were established, showing successive (partial)stacks,(‘control’ shown with a circle) ) Here, f, y are d-evaluated environment
When returns with 4, we are back at act(g) Now, d-eval f, y+1 Finally, back in act(g), 4+5 evaluates 9, and the computation of the let* returns this value environment
Example: Evaluate See details of stack evolution next page environment
Act frame eval d-eval The final result, 10, moves down to #0 environment
Example: next page environment
Act frame eval d-eval Wrong! The final result is 9 environment
The source of the problem: A delay between • creation of a function value(from a functionexpression) • its application The bindings for its free variables available when it is applied may differ from those when it is created environment
Act framed-eval #2 and the binding x1 are gone! #3 is ontop of#1 Free variable: x(or worse, could be defined) environment
The source of the problem: A delay between • creation of a function value(from a functionexpression) • its application The bindings for its free variables available when it is applied may differ from those when it is created environment
What is the result? Right or wrong? environment
What happens after a call E(2)? environment
Can such phenomena also happen in C? environment
In dynamic scoping the free (global) variables in a function body are associated with bindings late: when resolution is performed The associations use the stack of frames, accessed in order of generation This allows interference! no relationship to static structure Static structure is nota reliable predictor of execution environment
Various problems of dynamic scoping: • Same calls of a function (with same arg value) may return different results (including error msgs in some) (lack of referential transparency) • Change of a formal parameter of a function may cause others to change their behavior • A function called by F may `see’ F’s parameters and locals– a breach of abstraction/security • Useless to perform static checks such as • Static type-check • Is a variable defined before being used? (free var check) unpredictable behavior, often unrelated to static structure environment
Manifestation of problems does not require • Higher-order functions • Recursion (Although it is more common when these are present) Conclusion: dynamic scoping is Simple, elegant, (quite) efficient,but wrong! now considered an error, not used in modern pl’s Recall: Results of resolutions depend on how environments are modified when a activation is entered What other options are there ? environment
Static scoping The problem (in dynamic): A delay between creation of a function value andits application • The bindings for its free variables available when it is applied are different from those when it is created The solution: • When a function value is created, the bindings for its free variables (from current environment) are attached to it • When it is applied, these bindings are used as base environment environment
A function value (static scoping): fv= <p;b;E> , where • p is the parameter(s) • b is the body • E is an environment with bindings for The triple <p;b,E>is called a (function) closure • Intended to be used later in various places (delayed evaluation) • A closed package that contains everything needed for its future evaluation(s) environment
Closure and environment creation: • When a function expression is evaluated in environment E, the value<pars;b,E’>is created E’ is derived from E • When a function value <pars;b,E’> is applied to args in environment E’’ • a frame F of bindings parsargs is generated • the activation executes in environment • When it terminates, the environment E’’ is restored The environment component of a function value is derived from that of its time &place of creation environment
Note: environment creation upon entrance to let, let* is unchanged For letrec --- below environment
Implementation of Environment creation: (in all implementations, the term function closure is used) • Fully computed at the time of creation: • The environment component of a function value is computed (when it is created)by copying the relevantbindings from the current environment: if E is current environment, • That of an activation is computed (before it starts) A common choice in implementations of functional pl’s environment
Using frames linked by references: (A common approach for imperative pl’s) • An environment is a (linked) list of frames, viewed as a stack: • An entry: a frame & a reference to the next entry [F1,&F2] [F2, &F3] ….. • The first entry in the list is the top of the stack The reference in an entry is its static parent or static pointer environment
An activation contain a reference to the top/first entry of its environment and a dynamic parent/pointer, to the next entry on the activation stack The dynamic and static parents, from an activation and its environment, maylead to unrelated activation and environment! activations environments env(a8) = f8, f4, f1, … f11 a8 f8 f4 f1 a1 environment
A function value: <p;b;&F> , where &F is a reference to a frame --- the top frame of the current environment at its time of creation The triple <p;b;&F> is also called afunction closure (note: it may contain extra bindings!) 4. implemented as push --- create an entry < F,&top(E)>, make it the new top (this reference is the static parent of F) Previous examples revisited: environment
act frame dynamic: static: A2 A1 A5 x+y A4 f, x+5 • A3 g, 2 Result: 10 passed down to A4, … A0 A0 3 [F0:{}; nill] environment
A0 A4 x+y A3 g, 3 A2 is now gone from stack Frame lives! A2 A1 f, 1 environment
A2 A1 A5 x+y Result 10 to A4 to A3; what next? A4 f, x+5 A3 g, 2 A0 3 [F0{}; nill] environment
Observations: • Assume (A;F) are current staticparent(F), dynamic parent(A) are not necessarily associated with each other • Whenanactivation dies, the frames of its associated environment may be referenced from liveactivations or function values 1. An activation popped from stack is gone (dead); its associated frame (often) lives on(where?) 2. Static parent may correspond to no live activation! (is that a problem?) environment
The environment structure is a tree (how do we know it is a tree, not a graph? ) • Current environment is a path from a node to the root • When an activation starts, a node is added to the tree, as a child of some existing node (& becomes top of current environment) • When an activation terminates, a different path becomes the current environment Nodes are removed from the tree only by garbage collection (when provably they are not referenced from any live entity) no explicit pop environment
Other kinds of blocks: Afunction value is a package, carrying its own environment, since it moves around and may be invoked in various places Aregular block (let, let*, letrec)has no need of such a mechanism • Upon entrance to new region, new bindings are added to current environment (static parent ~ dynamic parent!) • Upon exit, previous environment is restored let and let* are simple, letrec requires re-consideration environment
Rules for let: (entered in current environment E) • Evaluate defining expressions in current environment • Create the frame F for the defined variables • Evaluate the body (~ activation) in • Upon exit, restore E reflects the scope rule of let, the fact that it is not recursive What are the rules for let*? environment
For a letrec, the defining expressions need to be evaluated in the new environment! Assume is evaluated in environment E But, this solution does not work now! environment
The solution: Do the evaluation of the functions and the construction of the new environment in one step The requirement: After this step environment
A solution with assignable cells: (the Scheme implementation) What is the role of delayed evaluation here? Can you think of a purely functional solution? environment
Assume computation starts from activation A0, and frame [F0:{};nill] How will the activation stack and the environment structure evolve? Note: The final value true is returned from final activation #i to #i-1, … to #0. Those in middle just pass it on environment
The rules: environment
For letrec: environment
Comments: • How does a computation start? A zero’th activation, with initial environment which may be empty, or not (depending on pl/implementation) • The model as described can explain activations and binding management in most languages, including interactive mode in functional languages however, the global environment and define in Scheme behave differently (still based on environments) environment
Functions in a pl exist on three levels: • L1: Function expressions (static) • L2: functionvalues (dynamic) • L3: activations of function values (dynamic) The relationships L1L2 , L2L3 are 1-m: • Many values may be created from an expression • Many activations of a value may occur, even be live simultaneously The distinction between L1, L2 is not evident in substitution model and in dynamic scope environment
Is behavior under static scoping compatible with static structure? Denote: • For a use u(x) in program: declu(u(x)) – the declaration d(x) that statically binds it (static) • For a binding b generated at run-time for a declaration d(x) : declb(b)=d(x) (dynamic) • For a use u(x) : resol(u(x)) – the binding returned by resolving it in current environment (dynamic) Claim1: declb(resol(u(x)))= declu(u(x)) environment
Meaning of arrows: resolves to static binding generated for xv1 d(x) xv2 d(x) xv3 xv4 u(x) u(x) environment
Can prove a stronger statement: If a binding xv is generated for declaration of x for a new activation, Then for every use of x in the scope of this declaration, its resolution always returns v let f = let x = 3 in lambda y.x+y;; …. f 5 returns 8 (always) Static structure is a reliable predictor of execution environment
Static scope avoids the problems of dynamic scope: • Calls of a function with same arguments behave the same – referential transparency holds • Change of formal parameter of a function does not change behavior (in activations of other functions) -- no surprises • function is a black box – no external function may observe values of locals • Useful to perform static check: • that a use is in scope of a declaration guarantees: resolution never returns unbound variable • Static type-checking guarantees absence of run-time type errors environment