Com S 342 meeting -*- Outline -*- * Dynamic Scope and Dynamic Assignment (5.7) Note: try to draw stacks that GROW DOWN the page, as this will be easier for all concerned. ** utility of dynamic constructs explain what happens below ------------------------------------------ UTILITY OF DYNAMIC SCOPE/ASSIGNMENT (5.7) changing implicit parameters: let stdout = port in p(1,2) let font = italic in format(mytext) stdout := port during p(1,2) font := italic during format(mytext) exception handlers: try { // Java syntax mydata.read(System.in); return mydata.average(); } catch (NoDataException e) { System.err.println("no data!"); return POSITIVE_INFINITY; } catch (ArithmeticExecption a) { System.err.println("no data!"); return POSITIVE_INFINITY; } ------------------------------------------ Q: Why couldn't stdout simply be a global variable? want to restore it back to standard binding when done modularity? (but you need access to it anyway) Q: Why not pass these around as parameters? how many parameters do you want to have for p? Q: What's like this in the Unix shell? I/O redirection Q: Why wouldn't the handlers be bound statically? call sites determine what to do. ** dynamic scope (5.7.1) *** background Historically, this arose from the first LISP interpreters, because it was easier to write this instead of making closures for procedures... (Current LISP interpreters and compilers use static scope.) Still used in operating system command languages (Unix shell), and various other interpreters (TeX, ...) *** domains ------------------------------------------ DOMAINS FOR DYNAMIC SCOPING Environment = -> Denoted-Value Denoted-Value = Cell(Expressed-Value) Expressed-Value = Number + + List(Expressed-Value) + Void Procedure-Text = ------------------------------------------ ... Procedure-Text ... prim-proc + proc Note that proc is from the abstract syntax! Q: What helper functions come with these domains? key ones are procedure-text->expressed : (-> (Procedure-Text) Expressed-Value) expressed->procedure-text : (-> (Expressed-Value) Procedure-Text) also need, to help the type checker, since proc records need to be converted from a parsed-exp to a procedure-text: proc->procedure-text : (-> (proc) Procedure-Text) *** interpreter Start with the clause for procedures Q: Where will the procdure get its environment then? ------------------------------------------ INTERPRETER WITH DYNAMIC SCOPE (5.7.1) (define eval-exp (lambda (exp env) (variant-case exp (lit (datum) (number->expressed datum)) (varref (var) (denoted->expressed (apply-env env var))) (app (rator rands) (let ((proc (expressed->procedure-text (eval-exp rator env))) (args (eval-rands rands env))) (proc (formals body) (varassign (var exp) (void->expressed (cell-set! (apply-env env var) (eval-exp exp env)))) ;; ... (else (error "Invalid abstract syntax:" exp))))) ------------------------------------------ ... (apply-proc proc args env))) ... (procedure-text->expressed (proc->procedure-text exp))) Note that what is returned in the case of the procedure is the abstract syntax tree, not a closure! ------------------------------------------ APPLY-PROC FOR DYNAMIC SCOPE ;; abstract syntax of procedure texts (define-record proc (formals body)) ;; dynamic scoping version (define apply-proc (lambda (proc args (variant-case proc (prim-proc (prim-op) (apply-prim-op prim-op args)) (proc (formals body) (eval-exp body (extend-env formals (map expressed->denoted args) (else (error "Invalid procedure-text:" proc))))) ------------------------------------------ Q: What is the domain Expressed-Value again? ... Number + Procedure-Text + List(Expressed-Value) + Void It's a bit strange having a parse tree as part of the values of the language, that should be a tip-off. Note below that the environment, since it's not in the closure, has to be passed to apply-proc, and it's used there. ... current-env) ; add to the lambda ... current-env))) *** behavior ------------------------------------------ BEHAVIOR WITH DYNAMIC SCOPE let three = 3 in let add3 = proc(i) +(i, three); three = 5 in *(three, add3(2)) ------------------------------------------ draw pictures of environments with static and dynamic scoping for this example. ------------------------------------------ DEFINITIONS A *dynamic binding* is extant References to a dynamically-bound variable refer to the ------------------------------------------ ... during the evaluation of the body associated with the binding form. ... most recent extant binding of that variable. Q: What's the difference from static scoping? ------------------------------------------ FOR YOU TO DO ;; Fig 5.7.3 let a = 3 in let p = proc() +(x, a); f = proc(x, y) *(p(), y); a = 5 in *(a, f(let a = 2 in a, 1)) TO DO: What is the value with dynamic scoping? Does it make sense with static scoping? ------------------------------------------ Q: What kind of scoping does the Unix shell use? LaTeX? APL? *** optimizing the interpreter, pages 169 (bottom) to 171 (skip) Q: Which is faster for variable access, dynamic or static? static has offset access, for dynamic, have to do a linear search through the env... This is called *deep binding*. Q: Is there any way to speed up variable access with dynamic scope? The idea is to avoid a long search for a variable, by quickly finding (hashing to) the variable name's bindings, and taking the most recent. This is called *shallow binding*. See figure 5.7.2 and exercise 5.7.5 ... *** funarg problem ------------------------------------------ THE FUNARG PROBLEM let G = 6.670e-11 in let gravForce = proc(m1) proc(r) proc(m2) /(*(G, *(m1, m2)), *(r,r)) in ((gravForce(5.96e24))(6.37e6))(68) ------------------------------------------ Q: what will happen with dynamic scope? draw a picture *** reasoning problems (pages 173ff) Q: Is alpha conversion valid with dynamic scope? beta? eta? ------------------------------------------ COUNTER-EXAMPLE let a = 3 in let p = proc() +(x, a); f = proc(x, y) *(p(), y); a = 5 in *(a, f(let a = 2 in a, 1)) vs. let a = 3 in let p = proc() +(x, a); f = proc(x, a) *(p(), a); %% *** a = 5 in *(a, f(let a = 2 in a, 1)) ------------------------------------------ For beta counter-example, consider the difference between f(let a = 2 in a, 1) and let a = 2 in f(a, 1) For eta counter-example, consider proc(i) f(i) Is that the same as f? Q: Could you do type checking at compile time if have dynamic scope? ** dynamic assignment *** idea ------------------------------------------ EXTENT AND SCOPE def: the *extent* of a binding is It can be either - indefinite = - dynamic = ALTERNATIVES scope binding \ extent indefinite dynamic lexical dynamic ------------------------------------------ ... how long it is retained. ...as long as needed (as in a closure, the bindings are kept) ... discarded when leave the binding form (as in dynamic scoping) Present the table as if you were analyzing the problem ... static dynamic assignment (= fluid binding) (global vars) dynamic scope *** syntax ------------------------------------------ DYNAMIC ASSIGNMENT Concrete syntax: ::= := during Examples: directory := codedir during compile() let x = 4 in let p = proc(y) +(y, x) in +(x := 7 during p(1), p(2)) abstract syntax: (define-record dynassign (var exp body)) ------------------------------------------ Q: What environment is used to evaluate each ? *** semantics Q: How would you implement this? ------------------------------------------ IMPLEMENTATION (define eval-exp (lambda (exp env) (variant-case exp (lit (datum) (number->expressed datum)) ;; ... (dynassign (var exp body) (let ((v-cell (apply-env env var)) (saved-cell (make-cell (eval-exp exp env)))) ------------------------------------------ ... (cell-swap! v-cell saved-cell) (let ((value (eval-exp body env))) (cell-swap! v-cell saved-cell) value)))))) Q: Why does the cell-swap! come after? because need to evaluate the expression in the environment with the binding desired ** evaluation Q: Which is better: dynamic assignment or dynamic scoping? dynamic assignment, has fewer drawbacks, still has lexical scope, but achieves same effects