The Environment Model* Jed Liu CSE 341 11 April 2003 *Adapted from Andrew Myers, CS 312, Spring 2002, Lec. 16 at Cornell University.
Important Note You will NOT be tested on this.... ....but knowing it will probably make understanding a future topic (closures) a lot easier.
Environment Model A method for reasoning about computation in LISP. Makes explicit the environment in which computation is being performed.
The Environment The environment is a map from variables to values. All computation is performed with respect to an environment. Example: (let* ((x 2) (y "hello") (f #'(lambda (z) x))) (funcall f (+ x (length y)))) Evaluate (funcall f (+ x (length y))) in the environment an anonymous function x: 2 y: "hello" f: #'(lambda (z) x)
Environment Frames A frame is a set of variables and their values. Each frame has a parent pointer that points to a parent environment. An environment is a linked list list of frames ending with the null environment (equivalent to NIL for LISP linked lists), sometimes called the global environment. The global environment implicitly contains the bindings for everything visible in the initial LISP environment.
Variables Variables are evaluated with respect to some environment. To do this, start with the last binding added to the environment and walk towards the nil. Evaluating x in this environment yields 3: nil x : 2 y : 4 x : 3 Blue boxes: bindings env z : 3
Let Expressions To evaluate (let ((x1 e1) ... (xn en)) e): Evaluate, in left-to-right order, all the ei’s in the current environment. Extend the current environment with bindings that maps each xi to the value of ei. Evaluate e in the extended environment. Restore the old environment (i.e., remove the binding for the xi’s). Return the value of e.
Let Example (let ((x '(1 2))) (car x)) current env nil
Let Example (let ((x '(1 2))) (car x)) 2 1 nil current env 1. Evaluating '(1 2) yields a pointer to a list in memory. 1 current env nil
Let Example (let ((x '(1 2))) (car x)) 2 1 current env x: nil 1. Evaluating '(1 2) yields a pointer to a list in memory. 1 2. Extend the environment with a binding for x. current env x: current env nil
Let Example (let ((x '(1 2))) (car x)) 2 1 current env x: nil 1. Evaluating '(1 2) yields a pointer to a list in memory. 1 2. Extend the environment with a binding for x. 3. Evaluate the body of the let in the new environment. x evaluates to a pointer to the list, so (car x) evaluates to the first element, namely 1. current env x: nil
Let Example (let ((x '(1 2))) (car x)) 2 1 current env x: nil 1. Evaluating '(1 2) yields a pointer to a list in memory. 1 2. Extend the environment with a binding for x. 3. Evaluate the body of the let in the new environment. x evaluates to a pointer to the list, so (car x) evaluates to the first element, namely 1. current env x: 4. Restore the old environment. current env nil
Let Example (let ((x '(1 2))) (car x)) 2 1 x: nil current env 1. Evaluating '(1 2) yields a pointer to a list in memory. 1 2. Extend the environment with a binding for x. 3. Evaluate the body of the let in the new environment. x evaluates to a pointer to the list, so (car x) evaluates to the first element, namely 1. x: 4. Restore the old environment. current env nil 5. Return the value we got: 1
Let* Expressions To evaluate: Do the same thing as you would for: (let* ((x e1) (y e2) (z e3)) e4) Do the same thing as you would for: (let ((x e1)) (let ((y e2)) (let ((z e3)) e4)))
Let* Example current env nil (let* ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) current env nil
Let* Example 3 4 current env nil (let* ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) 3 4 current env nil
Let* Example 3 4 current env x : current env nil (let* ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) current env 3 4 x : current env nil
Let* Example 3 4 current env x : nil (let* ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) current env 3 4 x : nil
Let* Example current env y : 3 4 current env x : nil (let* ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) current env y : current env 3 4 x : nil
Let* Example current env y : 3 4 x : nil (let* ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) current env y : 3 4 x : nil
Let* Example current env y : 3 4 x : Result is 3 nil (let* ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) current env y : 3 4 x : Result is 3 nil
Let* Example y : Restore last env 3 4 current env x : Result is 3 nil ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) y : Restore last env current env 3 4 x : Result is 3 nil
Let* Example y : 3 4 x : Restore original env Result is 3 current env ((x '(3 . 4)) (y (cons x x))) (car (cdr y))) (let ((x '(3 . 4))) (let ((y (cons x x))) (car (cdr y)))) y : 3 4 x : Restore original env Result is 3 current env nil
Dynamic scope: Perl, Python, BASIC Functions (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) How do we make sure the environment has the (correct) binding for x? We must keep track of the environment at the point where the function was evaluated. Function evaluation: #'(lambda (z) x), not (funcall f (length x)) We create a closure A pair of a function and its environment Static scope: ML, Java, Scheme, … Dynamic scope: Perl, Python, BASIC
Functions To evaluate a function #'(lambda (x) e), create a closure out of the function and the current environment and return a pointer to the closure. nil x : 2 y : 4 current env
Creating Closures To evaluate a function #'(lambda (x) e), create a closure out of the function and the current environment and return a pointer to the closure. (lambda (x) e) nil x : 2 y : 4 current env I’ll draw closures using yellow. Result
Function Example nil (lambda (z) x) x : 2 current env (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) (lambda (z) x) x : 2 current env nil
Function Example nil f: (lambda (z) x) current env x : 2 (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) f: (lambda (z) x) current env x : 2 nil
Function Example nil "bye" x : current env f: (lambda (z) x) x : 2 (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) "bye" current env x : f: (lambda (z) x) x : 2 nil
Function Example nil "bye" x : current env f: (lambda (z) x) x : 2 (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) "bye" current env x : f: (lambda (z) x) x : 2 nil
Function Calls To evaluate (e0 e1 e2 ... en): Evaluate e0 – you should get a pointer to a closure. Evaluate e1, e2, ..., en to values, in left-to-right order. Save the current environment – we’ll come back to it after the function call. Extend the environment of the closure, mapping the formal arguments to the actual arguments. Evaluate the body of the function within the extended environment – this gives us our result value. Restore the old environment (saved in step 3). Return the result.
Function Call Example nil 1. Evaluate e0, e1 "bye" x : current env f: (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) 1. Evaluate e0, e1 "bye" current env x : f: (lambda (z) x) x : 2 nil
Function Call Example nil 1. Evaluate e0, e1 2. Save environ. "bye" (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) 1. Evaluate e0, e1 2. Save environ. "bye" saved env x : f: (lambda (z) x) current env x : 2 nil
Function Call Example nil 1. Evaluate e0, e1 2. Save environ. (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) 1. Evaluate e0, e1 2. Save environ. 3. Extend env with actual "bye" saved env x : f: (lambda (z) x) current env z : 3 x : 2 nil
Function Call Example nil 1. Evaluate e0, e1 2. Save environ. (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) 1. Evaluate e0, e1 2. Save environ. 3. Extend env with actual 4. Evaluate body (result: 2) "bye" saved env x : f: (lambda (z) x) current env z : 3 x : 2 nil
Function Call Example nil 1. Evaluate e0, e1 2. Save environ. (let* ((x 2) (f #'(lambda (z) x))) (let ((x "bye")) (funcall f (length x)))) 1. Evaluate e0, e1 2. Save environ. 3. Extend env with actual 4. Evaluate body (result: 2) 5. Restore env. (result: 2) "bye" current env x : f: (lambda (z) x) z : 3 x : 2 nil