Last time: * Environment model of evaluation * Names represent locations, not values. ENVIRONMENT: sequence of FRAMES FRAME: set of BINDINGS (multiple bindings in same frame dont make sense) BINDING: variable name and its value We saw rules for: * variable lookup -- first frame of environment that has the variable * define ----------- add a binding to the top-level frame (the LAST one in any environment) * set! ------------- change the binding for the specified variable. * method ----------- build a procedure object: > parameter list > code body > pointer to environment that method was evaluated in (procedure was created in) * Evaluate --------- Eval subexpr's in current env, then apply first to rest * Apply: ----------- > Make a new frame with arg-name --> arg-value bindings > Eval body in the env with - first frame = argument bindings - rest = function's saved environment. ---------------------------------------------------------------------- (define (make-mult ) (method ((n )) (method ((x )) (* x n)))) The global environment is pretty big: Only one frame, But that frame has all the built-in functions in it, and result of any defines +----------------------------------------+ | + : {add} | | * : {mult} | | ... | | make-mult: ----------------------------+----> {}{} Params: ((n )) | | Body: (method | | ((x ))(* x n)) +----------------------------------------+ >> Env pointer back to global env Now, if we do (define (double ) (make-mult 2)) we build a new frame which points to the global envt (because that is where procedure we are applying points to): +-----------------------------------+ | n : 2 | +-----------------------------------+ and add to the global env a binding for double which is a closure {}{} Params: ((x )) Body: (* x n) >> Points to the environment with n:2 ---------------------------------------------------------------------- If we (define (triple ) (make-mult 3)) we'd get another frame +-----------------------------------+ | n : 3 | +-----------------------------------+ The procedure object for triple is like that for double, but with a different environment for n. >> Write the code object -- could be same code, could just "print the same", different environment!! >> Enter triple into the global environment Each procedure that we create this way has its *own* *private* environment, with just n in it. * Nobody else sees n ---------------------------------------------------------------------- (define (twice ) (make-mult 2)) builds *another* such environment. NOT the same [why?] ---------------------------------------------------------------------- At this point we can precisely specify how BIND works. (bind VARS BODY) where VARS = ((var1 val1) (var2 val2) ... (varn valn)) Can be thought of as follows: (bind VARS BODY) ==> ((method (vars ... varn) BODY) val1 ... valn) Example: (bind (((x ) 5)) (print x) (+ x 10)) is equivalent to: ((method ((x )) (print x) (+ x 10)) 5) This BIND creates a private frame, containing the binding x:5. ---------------------------------------------------------------------- State: functions that return different values, even when called with same arguments. If (barney 23) doesn't always return the same value, then barney has state. THREE EXAMPLES: [important for prelim #2 and final] Global state - global No state - none Local state - local (define (var ) 0) (define (global ) (method ((amt )) (set! var (+ var amt)) var)) (define (none ) (method ((amt )) (bind (((var ) 0)) (set! var (+ var amt)) var))) (define (local ) (bind(((var ) 0)) (method (( amt)) (set! var (+ var amt)) var))) (global 10) == ????? depends on what the value of var is! if var = 10 then (global 10) ==> 20 as long as no intervening computation! e.g., var = 10 (call-any-procedure-at-all) (global 10) ==> ????? because computation might have changed VAR (none 10) ==> 10 always! the local variable VAR is created with value 0 on each call to none, rebound to 10, and then returned. (local 10) ==> ???? depends on the value of the local state variable VAR if (local 0) ==> 10 then (local 10) ==> 20 even if intermediate computation, as long as the intermediate thing does not call local. local is the ONLY procedure that can change the local state variable VAR. THESE THREE EXAMPLES CONTRAST: Global state - global No state - none Local state - local -------------------------------------------------------------------------- A simple "account", which only accepts deposits (though you could make negative ones)... (define (make-account ) (method ((balance )) (method ((amt )) (set! balance (+ balance amt)) balance))) (define betty-acct (make-account 10)) (define barney-acct (make-account 10)) What is the type of these accounts? (barney-acct 20) ==> 30 (betty-acct 100) ==> 110 Barney and Betty now have separate accounts, with different balances! ---------------------------------------------------------------------- LOCAL STATE: * Packaging up state variables and their associated computation. * Another abstraction mechanism. In general we use local state variables rather than global ones because code is "safer", in the sense that only certain procedures can have access to these local variables. Think of "data objects with local state". One way of organizing a system that has objects with local state is the message passing style that we saw earlier in the semester. Now each object not only knows how to process certain messages, it also has local state variables that are particular to each INSTANCE or version of the object of a given TYPE. (define (make-account ) (method ((balance )) (method ((operation )) (cond ((= operation deposit:) (method ((amt )) (if (> amt 0) (set! balance (+ balance amt)) (print "Ahah! Can't deposite a negative amount!")))) ((= operation withdraw:) (method ((amt )) (if (and (> amt 0) (>= balance amt)) (set! balance (- balance amt)) (print "Your check just bounced!")))) (= operation balance:) balance) (else (error "Unknown operation")))))) ---------------------------------------------------------------------- Today's concept: LOCAL STATE * Package a computation with state variables. * Limit how variables can be changed. Resist Temptation: * Don't use set! unless necessary. * If you use it, package it up in a function as local state * AVOID GLOBAL VARIABLES!