Dynamic Assignment

Quiz:

Lexical scope: What is the output?

When p is applied, free vars in p are
bound in p's creation environment

(let ([a 3])
  (let ([p (proc (x) ( + x a))]
	[a 5])
    (* a (p 2))))




Dynamic Scope:

When p is applied, free vars in p are
bound in p's application environment

This is DYNAMIC BINDING

A dynamic binding is extant during the evaluation
of the body associaed with the binding form.
References to a dynamically-bound variable refer
to the MOST RECENT extant binding of that variable

EASIER to IMPLEMENT
MORE DIFFICULT to REASON ABOUT

Dynamic Binding: What is the output?

(let ([a 3])
  (let ([p (proc (x) ( + x a))]
	[a 5])
    (* a (p 2))))

(define eval-exp
  (lambda (exp env)
    (variant-case exp
      (lit (datum) datum)
      (varref (var)
	(cell-ref (apply-env env var)))
      (app (rator rands)
        (let ((proc (eval-exp rator env))
              (args (eval-rands rands env)))
          (apply-proc proc args env)))
      (if (test-exp then-exp else-exp)
        (if (true-value?
	      (eval-exp test-exp env))
            (eval-exp then-exp env)
            (eval-exp else-exp env)))
      (proc (formals body) exp)
      (varassign (var exp)
        (cell-set!
	  (apply-env env var)
	  (eval-exp exp env)))
      (else
	(error "Invalid abstract syntax:" exp)))))

(define apply-proc
  (lambda (proc args current-env)
    (variant-case proc
      (prim-proc (prim-op)
	(apply-prim-op prim-op args))
      (proc (formals body)
        (eval-exp body 
          (extend-env formals
	    (map make-cell args) current-env)))
      (else
	(error "Invalid procedure:" proc)))))



With dynamic scope, when a binding is added 
to an environment, it remains in effect
(is extant) until all subsequent bindings are 
removed. 

This is 
LIFO or stack behavior. Therefore:
instead of carrying the environment around all the 
time by passing it as an argument, let's use a
global stack.


(define eval-exp
  (lambda (exp)
    (variant-case exp
      (lit (datum) datum)
      (varref (var)
	(cell-ref (lookup-in-env var)))
      (app (rator rands)
        (let ((proc (eval-exp rator))
              (args (eval-rands rands)))
          (apply-proc proc args)))
      (if (test-exp then-exp else-exp)
        (if (true-value?
	      (eval-exp test-exp))
            (eval-exp then-exp)
            (eval-exp else-exp)))
      (proc (formals body) exp)
      (varassign (var exp)
        (cell-set!
	  (lookup-in-env var) (eval-exp exp)))
      (else
	(error "Invalid abstract syntax:" exp)))))

(define eval-rands
  (lambda (rands)
    (map (lambda (exp) (eval-exp exp)) 
         rands)))

(define apply-proc
  (lambda (proc args)
    (variant-case proc
      (prim-proc (prim-op)
	(apply-prim-op prim-op args))
      (proc (formals body)
        (push-env!
	  formals
	  (map make-cell args))
        (let ((value (eval-exp body)))
          (pop-env!)
          value))
      (else (error
	      "Invalid procedure:" proc)))))


Recursive procedures may be bound by let! no
special handling needed for recursive procedures. 

However, NFL

How deep is a variable binding in the stack?
No way to tell in advance. This single stack
approach is called DEEP BINDING.

Variable referencing is inefficient with deep
binding. 

Alternative:
	A separate stack for each variable name? 
Shallow binding.

Top binding is always the one you want. Constant
Access time.

However, when procedures have several arguments,
pushing and popping a NUMBER of stacks is less
efficient


So many tradeoffs......




With dynamic binding:

1. alpha, beta, and neta conversions 
are not valid

2. Binding variables with dynamic scope has GLOBAL 
effect; lexical scope has LOCAL effect



DYNAMIC ASSIGNMENT


Consider:
	Assignment statements, with dynamic extent 
to bindings that have lexical scope!!

(letDynamic ([var1 exp1]
             [var2 exp2]
             ...
             [varn expn])
;;;;during 
	body)

These assignments are valid during the evaluation
of body.


for example, 
(letdynamic ([standardoutput port])
;;        during 
	(p 1 2))

You don't have to pass procedure p the output
stream/port 

In fact, you don't have to pass the output
stream/port to
procedure p OR ANY OTHER PROCEDURES THAT p MAY
CALL  

Very convenient.

After all the programmer does not even need to
know  if p calls any procedures that do output.

dynamic assignment is also perfect for
implementing exception handling

For example: 
DURING execution of procedure foo
we would like to have an exception handler 
handle divide by zero by returning a LARGE number, 
the rest of the time, we want the program to
abort.
 
Dynamic assignment is perfect for this.

    

Sushil Louis
Last modified: Fri Dec 10 16:23:25 PST 1999