Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Lecture 19 Review last lecture on evaluator, Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)

Similar presentations


Presentation on theme: "1 Lecture 19 Review last lecture on evaluator, Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)"— Presentation transcript:

1

2 1 Lecture 19 Review last lecture on evaluator, Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)

3 2 Compiler vs. Interpreter Command Processing Unit The Computer Program in Low Level Machine Language Program in High Level Language Transformation The Programmer T

4 3 A Compiler CPU Machine Level Program High Level Program C Inputs Outputs The Compiler turns the high level program instructions to Instructions understood by the machine.

5 4 An Interpreter CPU High Level Program I Inputs Outputs The Interpreter is a machine level program, which interprets and executes the high level program line after line…

6 5 The Metacircular Evaluator CPU Scheme Program Inputs Outputs The Metacircular Evaluator is an Interpreter for Scheme on a machine whose machine language is Scheme. ME

7 6 The Environment Evaluation Model 1.To evaluate a combination (a compound expression other than a special form), evaluate the subexpressions and then apply the value of the operator subexpression to the values of the operand subexpressions. 2.To apply a compound procedure to a set of arguments, evaluate the body of the procedure in a new environment. To construct this environment, extend the environment part of the procedure object by a frame in which the formal parameters of the procedure are bound to the arguments to which the procedure is applied.

8 7 The Eval/Apply Cycle

9 8 The Meta-Circular Evaluator: Eval (define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env)) ((quoted? exp) (text-of-quotation exp)) ((assignment? exp) (eval-assignment exp env)) ((definition? exp) (eval-definition exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) ((begin? exp) (eval-sequence (begin-actions exp) env)) ((cond? exp) (eval (cond->if exp) env)) ((application? exp) (apply (eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression type -- EVAL" exp)))) Special forms

10 9 The Meta-Circular Evaluator: Apply (define (apply procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments (procedure-environment procedure)))) (else (error "Unknown procedure type" procedure))))

11 10 Sample Execution (define the-global-environment (setup-environment)) (driver-loop) ;;; M-Eval input: (define (append x y) (if (null? x) y (cons (car x) (append (cdr x) y)))) ;;; M-Eval value: ok ;;; M-Eval input: (append '(a b c) '(d e f)) ;;; M-Eval value: (a b c d e f)

12 11 Read-Eval-Print Loop (define input-prompt ";;; M-Eval input:") (define output-prompt ";;; M-Eval value:") (define (prompt-for-input string) (newline) (newline) (display string) (newline)) (define (announce-output string) (newline) (display string) (newline)) (define (driver-loop) (prompt-for-input input-prompt) (let ((input (read))) (let ((output (eval input the-global-env))) (announce-output output-prompt) (user-print output))) (driver-loop))

13 12 Read returns a cons structure to eval (eval ) 2x+ define 54+ x (eval ) Eval must now evaluate the expression and return a value…

14 13 Plan of Our Presentation We will begin with a simple variant of the interpreter. In describing it we will break many of the abstraction layers and levels trying to give you a feel of what is really going on. This variant is similar to the MCE we will eventually construct, but not identical. We start by extending a simple “calculator” like evaluator to allow saving values in variables…I.e. have an environment.

15 14 Start Simple: Names Extend a “calculator” to store intermediate results as named values (define x (+ 4 5)) store result as x (+ x 2) use that result Store bindings between names and values in a table

16 15 (define (definition? exp) (tag-check exp 'define)) (define (eval exp) (cond ((number? exp) exp) ((symbol? exp) (lookup exp)) ((sum? exp) (eval-sum exp)) ((definition? exp) (eval-definition exp)) (else (error "unknown expression " exp)))) (define environment (make-frame nil nil)) (define (lookup name) (lookup-variable-value name environment)) (define (eval-definition exp) (let ((name (cadr exp)) (defined-to-be (eval (caddr exp)))) (add-binding-to-frame! name defined-to-be environment) ‘undefined))

17 16 The Environment Representation list of values list of variables frame xy4 5 Note: we are assuming an environment Consisting of a single frame

18 17 (define (make-frame variables values) (cons variables values)) (define (lookup-variable-value var frame) (define (scan vars vals) (cond ((null? vars) (error "Unbound variable" var)) ((eq? var (car vars) (car vals)) (else (scan (cdr vars) (cdr vals))))) (let ((vars (car frame)) (vals (cdr frame))) (scan vars vals))) (define (add-binding-to-frame! var val frame) (set-car! frame (cons var (car frame))) (set-cdr! frame (cons val (cdr frame))))

19 18 Evaluation Example (eval '(define x (+ 4 5))) (eval '(+ 4 5)) (eval 4) ==> 4 (eval 5) ==> 5 ==> 9 ==> undefined (eval '(+ x 2)) (eval 'x) ==> 9 (eval 2) ==> 2 ==> 11 (define x (+ 4 5)) (+ x 2) 9x names values

20 19 Things to observe Use scheme function symbol? to check for a name the reader converts sequences of characters like "x" to symbols in the parse tree Can use any implementation of the environment, in particular we could use put and get from previous lectures. eval-definition recursively calls eval on the second subtree but not on the first one eval-definition returns a special undefined value

21 20 Conditionals and If Extend the calculator to handle conditionals and if: (if (> y 6) (+ y 2) 15) > an operation that returns a boolean if an operation that evaluates the first subexp, checks if value is true or false

22 21 (define (greater? exp) (tag-check exp ’>)) (define (if? exp) (tag-check exp 'if)) (define (eval exp) (cond ((number? exp) exp) ((symbol? exp) (lookup exp)) ((sum? exp) (eval-sum exp)) ((greater? exp) (eval-greater exp)) ((definition? exp) (eval-definition exp)) ((if? exp) (eval-if exp)) (else (error "unknown expression " exp)))) (define (eval-greater exp) (> (eval (cadr exp)) (eval (caddr exp))))

23 22 (define (eval-if exp) (if (true? (eval (if-predicate exp))) (eval (if-consequent exp)) (eval (if-alternative exp)))) (define (if-predicate exp) (cadr exp)) (define (if-consequent exp) (caddr exp)) (define (if-alternative exp) (if (not (null? (cdddr exp))) (cadddr exp) 'false)) (define (true? x) (not (or (eq? x #f) (eq? x ‘false))) (define (false? x) (or (eq? x #f) (eq? x ‘false)) (eval '(define y 9)) (eval '(if (> y 6) (+ y 2) 15))

24 23 We are just walking through a tree … (eval ) 6y> if 6y>2y+ 15 Then (eval ) or (eval 15 ) 2y+

25 24 Evaluation of (eval '(if (> y 6) (+ y 2) 15)) (eval '(> y 6)) (eval 'y) ==> 9 (eval 6) ==> 6 ==> #t (eval '(+ y 2)) (eval 'y) ==> 9 (eval 2) ==> 2 ==> 11

26 25 Things to observe eval-greater is just like eval-sum from page 1 recursively call eval on both argument expressions call scheme > to compute the value eval-if does not call eval on all argument expressions: call eval on the predicate call eval on the consequent or on the alternative but not both

27 26 Store operators in the environment Want to add lots of operators but keep eval short Operations like + and > are similar evaluate all the argument subexpressions perform the operation on the resulting values Call this standard pattern an application Implement a single case in eval for all applications Approach: eval the first subexpression of an application put a name in the environment for each operation put a value if that name is a procedure apply the procedure to the operands

28 27 (define (application? exp) (pair? exp)) (define (eval exp) (cond ((number? exp) exp) ((symbol? exp) (lookup exp)) ((definition? exp) (eval-definition exp)) ((if? exp) (eval-if exp)) ((application? exp) (apply (eval (car exp)) (map eval (cdr exp)))) (else (error "unknown expression " exp)))) ;; rename scheme’s apply so we can reuse the name (define scheme-apply apply) (define (apply procedure arguments) (if (primitive-procedure? procedure) (apply-primitive-procedure procedure arguments) (error "operator not a procedure: " procedure))) (define (apply-primitive-procedure proc args) (scheme-apply (primitive-implementation proc) args))

29 28 (define prim-tag 'primitive) (define (make-primitive scheme-proc) (list prim-tag scheme-proc)) (define (primitive-procedure? exp) (tag-check exp prim-tag)) (define (primitive-implementation prim) (cadr prim)) (define environment (make-frame nil nil)) (add-binding-to-frame! environment ’+ (make-primitive +)) (add-binding-to-frame! environment ’> (make-primitive >)) (add-binding-to-frame! environment 'true #t) (eval '(define z 9)) (eval '(+ 9 6)) (eval '(if true 10 15))

30 29 The Environment after first “define” symbol primitive scheme procedure > symbol primitive scheme procedure + z9 true#t > + Name Value

31 30 Evaluation of (+ 9 6) (eval '(+ 9 6)) (apply (eval +) (map eval '(9 6))) (apply '(primitive #[add]) (list (eval 9) (eval 6)) (apply '(primitive #[add]) '(9 6)) (scheme-apply (primitive-implementation '(primitive #[add])) '(9 6)) (scheme-apply #[add] '(9 6)) 15

32 31 Evaluation of (if true 10 15) (eval '(if true 10 15)) (eval-if '(if true 10 15)) (if (true? (eval 'true))...) (if (true? (lookup 'true)))...) (if (true? #t)...) (eval 10) 10 Apply is never called!

33 32 Things to observe Applications must be the last case in eval No tag check Applications evaluate all subexpressions Expressions that need special handling, like if, get their own case in eval

34 33 The Environment as an Explicit Parameter Change from (eval '(+ 6 4)) to (eval '(+ 6 4) environment) All procedures that call eval have extra argument lookup and define use the environment from the argument No other change from earlier evaluator

35 34 The Environment as an Explicit Parameter ;This change is boring! Exactly the same functionality as #4. (define (eval exp env) (cond ((number? exp) exp) ((symbol? exp) (lookup exp env)) ((definition? exp) (eval-defininition exp env)) ((if? exp) (eval-if exp env)) ((application? exp) (apply (eval (car exp) env) (map (lambda (exp) (eval exp env)) (cdr exp)))) (else (error "unknown expression " exp)))) (define (lookup name env) (lookup-variable-value name env))

36 35 (define (eval-define exp env) (let ((name (cadr exp)) (defined-to-be (eval (caddr exp) env))) (add-binding-to-frame! name defined-to-be env) ‘undefined)) (define (eval-if exp env) (if (true? (eval (if-predicate exp) env)) (eval (if-consequent exp) env) (eval (if-alternative exp) env))) The Environment as an Explicit Parameter (Continued)

37 36 Defining New Procedures Want to add new procedures For example, a scheme program: (define twice (lambda (x) (+ x x))) (twice 4) Strategy: Add a case for lambda to eval –the value of lambda is a compound procedure Extend apply to handle compound procedures Implement environment model

38 37 (define (lambda? exp) (tag-check exp 'lambda)) (define (eval exp env) (cond ((number? exp) exp) ((symbol? exp) (lookup exp env)) ((define? exp) (eval-define exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (eval-lambda exp env)) ((application? exp) (apply (eval (car exp) env) (map (lambda (e) (eval e env)) (cdr exp)))) (else (error "unknown expression " exp)))) (define (eval-lambda exp env) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) (define (lambda-parameters exp) (cadr exp)) (define (lambda-body exp) (cddr exp)) (define (make-procedure parameters body env) (list 'procedure parameters body env)) p: b:

39 38 (define (apply procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments (procedure-env procedure)))) (else (error "Unknown procedure type -- APPLY" procedure)))) (define (compound-procedure? exp) (tag-check exp ‘procedure)) (define (procedure-parameters compound) (cadr compound)) (define (procedure-body compound) (caddr compound)) (define (procedure-env compound) (cadddr compound))

40 39 (define (eval-sequence exps env) (cond ((last-exp? exps) (eval (first-exp exps) env)) (else (eval (first-exp exps) env) (eval-sequence (rest-exps exps) env)))) (define (last-exp? seq) (null? (cdr seq))) (define (first-exp seq) (car seq)) (define (rest-exps seq) (cdr seq))

41 40 How the Environment Works Abstractly – in our environment diagrams: Concretely – our implementation (as in SICP) E2 x: 10 plus: (procedure...) E1 environment manipulation 3. list of values enclosing- environment list of variables frame xplus10 E2 procedure

42 41 Extending the Environment (extend-environment '(x y) '(4 5) E2) E1 E2 x: 10 plus: (procedure...) E3 x: 4 y: 5 Abstractly Concretely E2 list of values list of variables frame xy4 E3 5 E1

43 42 (define (extend-environment vars vals base-env) (if (= (length vars) (length vals)) (cons (make-frame vars vals) base-env) (if (< (length vars) (length vals)) (error "Too many arguments supplied" vars vals) (error "Too few arguments supplied" vars vals))))

44 43 "Scanning" the environment Look for a variable in the environment... Look for a variable in a frame... –loop through the list of vars and list of vals in parallel –detect if the variable is found in the frame If not found in frame (out of variables in the frame), look in enclosing environment

45 44 z9 true#t + twice Representing procedures (eval '(define twice (lambda (x) (+ x x))) GE) symbol primitive scheme procedure + symbol procedure symbol + symbol x

46 45 Representing the Environment (eval ‘(twice 4)) (extend-environment '(x) '(4) GE) GE x: 10 +: (primitive...) twice: (procedure..) E1 x: 4 Abstractly Concretely GE list of values list of variables frame x4 E1 x 10 + twice primitive

47 46 Representing the Environment (eval ‘(twice 5)) (extend-environment '(x) '(5) GE) GE list of values list of variables frame x4 E1 x 10 + twice primitive x5 E2

48 47 (define (lambda? exp) (tag-check exp 'lambda)) (define (eval exp env) (cond ((number? exp) exp) ((symbol? exp) (lookup exp env)) ((define? exp) (eval-define exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (eval-lambda exp env)) ((application? exp) (apply (eval (car exp) env) (map (lambda (e) (eval e env)) (cdr exp)))) (else (error "unknown expression " exp)))) (define (eval-lambda exp env) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) (define (lambda-parameters exp) (cadr exp)) (define (lambda-body exp) (cddr exp)) (define (make-procedure parameters body env) (list 'procedure parameters body env)) p: b:

49 48 (define (apply procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments (procedure-env procedure)))) (else (error "Unknown procedure type -- APPLY" procedure)))) (define (compound-procedure? exp) (tag-check exp ‘procedure)) (define (procedure-parameters compound) (cadr compound)) (define (procedure-body compound) (caddr compound)) (define (procedure-env compound) (cadddr compound))

50 49 (define (lookup-variable-value var env) (define (env-loop env) (define (scan vars vals) (cond ((null? vars) (env-loop (enclosing-environment env))) ((eq? var (car vars)) (car vals)) (else (scan (cdr vars) (cdr vals))))) (if (eq? env the-empty-environment) (error "Unbound variable" var) (let ((frame (first-frame env))) (scan (frame-variables frame) (frame-values frame))))) (env-loop env)) (define (frame-variables frame) (car frame)) (define (frame-values frame) (cdr frame)) (define (enclosing-environment env) (cdr env)) (define (first-frame env) (car env))

51 50 (define (define-variable! var val env) (let ((frame (first-frame env))) (define (scan vars vals) (cond ((null? vars) (add-binding-to-frame! var val frame)) ((eq? var (car vars)) (set-car! vals val)) (else (scan (cdr vars) (cdr vals))))) (scan (frame-variables frame) (frame-values frame)))) (define (eval-definition exp env) (let ((name (cadr exp)) (defined-to-be (eval (caddr exp) env))) (define-variable! name defined-to-be env) ‘undefined))

52 51 Initialization primitives and initial env. (define the-empty-environment '()) (define the-global-environment (setup-environment)) (define (setup-environment) (let ((initial-env (extend-environment (primitive-procedure-names) (primitive-procedure-objects) the-empty-environment))) (define-variable! 'true #t initial-env) (define-variable! 'false #f initial-env) initial-env))

53 52 (define primitive-procedures (list (list 'car car) (list 'cdr cdr) (list 'cons cons) (list 'null? null?) (list '+ +) ;; more primitives )) (define (primitive-procedure-names) (map car primitive-procedures)) (define (primitive-procedure-objects) (map (lambda (proc) (list 'primitive (cadr proc))) primitive-procedures))

54 53 SICP’s version of eval (define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env)) ((quoted? exp) (text-of-quotation exp)) ((assignment? exp) (eval-assignment exp env)) ((definition? exp) (eval-definition exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) ((begin? exp) (eval-sequence (begin-actions exp) env)) ((cond? exp) (eval (cond->if exp) env)) ((application? exp) (apply (eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression type -- EVAL" exp))))

55 54 Note some minor differences like (define (self-evaluating? exp) (cond ((number? exp) true) ((string? exp) true) (else false))) (define (variable? exp) (symbol? exp)) (define (list-of-values exps env) (if (no-operands? exps) '() (cons (eval (first-operand exps) env) (list-of-values (rest-operands exps) env))))

56 55 Is eval an iterative or recursive algorithm? It depends on the scheme program being evaluated An iterative algorithm in scheme (eval '(define odd (lambda (n) (odd (- n 2)))) GE) A recursive algorithm in scheme (eval '(define sum (lambda (n) (+ n (sum (- n 1))))) GE)

57 56 Base case and if check omitted from both algorithms to simplify the example (eval '(define odd (lambda (n) (odd (- n 2)))) GE) (eval '(odd 4) GE) [call apply, which creates E1: n = 4, then eval body of odd] (eval '(odd (- n 2)) E1) (apply (eval 'odd E1) (list (eval '(- n 2) E1))) [skip some steps in which (- n 2) ==> 2] (apply (list ’procedure '(n) '(odd (- n 2)) GE) '(2)) [apply creates E2: n = 2, then eval body of odd] (eval '(odd (- n 2)) E2)) No pending operations on the recursive call to eval

58 57 (eval '(define sum (lambda (n) (+ n (sum (- n 1))))) GE) (eval '(sum 4) GE) [call apply, which creates E1: n = 4, then eval body of sum] (eval '(+ n (sum (- n 1))) E1) (apply '(primitive #[add]) (list (eval 'n E1) (eval '(sum (- n 1)) E1))) [skip some steps in which (- n 1) ==> 3] (apply '(primitive #[add]) (list 4 (apply (eval 'sum E1) '(3)))) [apply creates E2: n = 3, then eval body of sum] (apply '(primitive #[add]) (list 4 (eval '(+ n (sum (- n 1))) E2)) There are pending operations on the recursive call to eval

59 58 Summary Cycle between eval and apply is the core of the evaluator eval calls apply with operator and argument values apply calls eval with expression and environment What is still missing from scheme ? Some special forms data types other than numbers and booleans

60 59 Note: Syntactic Abstraction Semantics What the language means Model of computation Syntax Particulars of writing expressions E.g. how to signal different expressions Separation of syntax and semantics: allows one to easily alter syntax eval/apply syntax procedures

61 60 Basic Syntax Routines to detect expressions (define (if? exp) (tagged-list? exp 'if)) (define (lambda? exp) (tagged-list? exp 'lambda)) (define (application? exp) (pair? exp)) Routines to get information out of expressions (define (operator app) (car app)) (define (operands app) (cdr app)) (define (first-operand args) (car args)) (define (rest-operands args) (cdr args)) Routines to build expressions (define (make-if predicate consequent alternative) (list 'if predicate consequent alternative))

62 61 Example – Changing Syntax Suppose you wanted a "verbose" application syntax: (CALL ARGS...) Changes – only in the syntax routines! (define (application? exp) (tagged-list? 'CALL)) (define (operator app) (cadr app)) (define (operands app) (cdddr app))

63 62 Implementing "Syntactic Sugar" Idea: Implement a simple fundamental "core" in the evaluator Easy way to add alternative/convenient syntax? "let" as sugared procedure application: (let (( ) ( )) ) ((lambda ( ) ) )

64 63 Detect and Transform the Alternative Syntax (define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env)) ((quoted? exp) (text-of-quotation exp))... ((cond? exp) (eval (cond->if exp) env)) ((let? exp) (eval (let->combination exp) env)) ((application? exp) (apply (eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression" exp))))

65 64 Implementing cond: Syntax procedures (define (cond-clauses exp) (cdr exp)) (define (cond-else-clause? clause) (eq? (cond-predicate clause) 'else)) (define (cond-predicate clause) (car clause)) (define (cond-actions clause) (cdr clause)) (cond ((= x 23) (+ x 1)) (else (- x 1))) (if (= x 23) (+ x 1) (- x 1))

66 65 Cond syntax (cond ((= x 23) (+ x 1)) (else (- x 1))) cond else 23x = 1x+ 1 x -

67 66 Transforming sequence of expressions to an expression (define (sequence->exp seq) (cond ((null? seq) seq) ((last-exp? seq) (first-exp seq)) (else (make-begin seq)))) (define (make-begin seq) (cons 'begin seq)) 2x * car y begin

68 67 Implementing cond (Cont.) (cond ((= x 23) (+ x 1)) (else (- x 1))) (if (= x 23) (+ x 1) (- x 1))

69 68 Implementing cond (define (cond->if exp) (expand-clauses (cond-clauses exp))) (define (expand-clauses clauses) (if (null? clauses) 'false ; no else clause (let ((first (car clauses)) (rest (cdr clauses))) (if (cond-else-clause? first) (if (null? rest) (sequence->exp (cond-actions first)) (error "ELSE clause isn't last -- COND->IF" clauses)) (make-if (cond-predicate first) (sequence->exp (cond-actions first)) (expand-clauses rest))))))

70 69 Details of cond syntax transformation (cond ((= x 23) (+ x 1)) (else (- x 1))) cond else 23x = 1x + 1x -

71 70 Details of cond syntax transformation ( expand-clauses else 23x = 1x + 1x - )

72 71 Details of cond syntax transformation first else 23x = 1x + 1x - rest

73 72 Details of cond syntax transformation 23x = 1x + else 1x - (make-if (expand-clauses ))

74 73 Details of cond syntax transformation 23x = 1x + 1 x - (make-if )

75 74 Details of cond syntax transformation 23x = 1x + 1 x - if

76 75 Named Procedures – Syntax vs. Semantics (define (foo ) ) Semantic implementation – just another define: (define (eval-definition exp env) (define-variable! (definition-variable exp) (eval (definition-value exp) env) env)) (define (definition-variable exp) (if (symbol? (cadr exp)) (cadr exp) (caadr exp))) (define (definition-value exp) (if (symbol? (cadr exp)) (caddr exp) (make-lambda (cdadr exp) ;formal params (cddr exp)))) ;body

77 76 Our Evaluator Implements Lexical Scoping Free variables in an application of procedure f get their values from the procedure where f was defined (by the appropriate lambda special form). Also called static binding (define (foo x y) (lambda (z) (+ x y z))) (define bar (foo 1 2)) (bar 3) bar : (lambda (z) (+ 1 2 z))

78 77 Lexical Scoping Environment Diagram (define (foo x y) (lambda (z) (+ x y z))) GE p: x y body: ( (z) (+ x y z)) foo: E1 x: 1 y: 2 bar: p: z body: (+ x y z) (+ x y z) | E2 => 6 z: 3 E2 (define bar (foo 1 2)) (bar 3) Will always evaluate (+ x y z) in a new environment inside the surrounding lexical environment.

79 78 Alternative Model: Dynamic Scoping Dynamic scope: –Look up free variables in the caller's environment rather than the surrounding lexical environment Example: (define x 11) (define bear (lambda (y) (+ x y))) (define (p x) (bear 20)) (p 9) => 29

80 79 Dynamic Scoping Environment Diagram (define (p x) (bear 20)) GE p: x body: (bear  p: (+ x y) | E2 => 29 x: 9 E1 (define (bear y) (+ x y)) (p 9) Will evaluate (+ x y) in an environment that extends the caller's environment. bear: p: y body: (+ x y  y: 20 E2 (define x 11) x:11

81 80 A "Dynamic" Version of Scheme (define (d-eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env))... ((lambda? exp) (make-procedure (lambda-parameters exp) (lambda-body exp) '*no-environment*)) ;CHANGE: no env... ((application? exp) (d-apply (d-eval (operator exp) env) (list-of-values (operands exp) env) env)) ;CHANGE: add env (else (error "Unknown expression -- M-EVAL" exp))))

82 81 A "Dynamic" Scheme – d-apply (define (d-apply procedure arguments calling-env) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments calling-env))) ;CHANGE: use calling env (else (error "Unknown procedure" procedure))))

83 82 Implement lazy evaluation Beyond Scheme – designing language variants: Lazy evaluation Complete conversion – normal order evaluator Upward compatible extension – lazy, lazy-memo

84 83 Normal Order (Lazy) Evaluation Alternative models for computation: Applicative Order: evaluate all arguments, then apply operator Normal Order: go ahead and apply operator with unevaluated argument subexpressions evaluate a subexpression only when value is needed to print by primitive procedure

85 84 Limitations of Applicative Order Evaluation Suppose we wanted to create: (define (unless condition usual-value exceptional-value) (if condition exceptional-value usual-value)) Which can be used in expressions such as: (unless (= b 0) (/ a b) (begin (display "exception: returning 0") 0)) This won't work in an applicative-order language! But Will work with Lazy Evaluation!

86 85 Strict vs. Non-Strict Arguments If the body of a procedure is entered before an argument has been evaluated we say that the procedure is non-strict in that argument. If the argument is evaluated before the body of the procedure is entered we say that the procedure is strict in that argument. Applicative order: strict in all arguments. Normal order: strict in arguments only in primitive procedures.

87 86 How to Implement Lazy Evaluation (define (foo x y)(+ x y)) GE p: x y body:(+ x y) foo: E1 x: “(+ 1 2)” y: “(+ 3 4)” (foo (+ 1 2) (+ 3 4)) Eval will not evaluate the parameters passed to apply! How are these delayed parameters passed? Via the binding in the extended environment E1!

88 87 How can we implement lazy evaluation? (define (l-apply procedure arguments env) ; changed (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure (list-of-arg-values arguments env))) ((compound-procedure? procedure) (l-eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) (list-of-delayed-args arguments env) (procedure-environment procedure)))) (else (error "Unknown proc" procedure))))

89 88 Lazy Evaluation – l-eval Most of the work is in l-apply ; need to call it with: actual value for the operator just expressions for the operands the environment... (define (l-eval exp env) (cond ((self-evaluating? exp) exp)... ((application? exp (l-apply (actual-value (operator exp) env) (operands exp) env)) (else (error "Unknown expression" exp))))

90 89 Actual vs. Delayed Values (define (actual-value exp env) (force-it (l-eval exp env))) (define (list-of-delayed-args exps env) (if (no-operands? exps) '() (cons (delay-it (first-operand exps) env) (list-of-delayed-args (rest-operands exps) env)))) (define (list-of-arg-values exps env) (if (no-operands? exps) '() (cons (actual-value (first-operand exps) env) (list-of-arg-values (rest-operands exps) env))))

91 90 Representing Thunks Abstractly – a thunk is a "promise" to return a value when later needed ("forced") Concretely – our representation: thunk envexp

92 91 Thunks – delay-it and force-it (define (delay-it exp env) (list 'thunk exp env)) (define (thunk? obj) (tagged-list? obj 'thunk)) (define (thunk-exp thunk) (cadr thunk)) (define (thunk-env thunk) (caddr thunk)) (define (force-it obj) (cond ((thunk? obj) (actual-value (thunk-exp obj) (thunk-env obj))) (else obj))) (define (actual-value exp env) (force-it (l-eval exp env)))

93 92 Lazy Evaluation – other changes needed Example – need actual predicate value in conditional if... (define (l-eval-if exp env) (if (true? (actual-value (if-predicate exp) env)) (l-eval (if-consequent exp) env) (l-eval (if-alternative exp) env))) Example – don't need actual value in assignment... (define (l-eval-assignment exp env) (set-variable-value! (assignment-variable exp) (l-eval (assignment-value exp) env) env) 'ok)

94 93 (l-eval ‘((lambda (x) (+ x x) ) (+ 1 1)) GE) (l-apply (actual-value ‘(lambda (x) (+ x x)) GE) ‘((+1 1 )) GE) (force-it (l-eval ‘(lambda (x) (+ x x)) GE)) ‘(procedure ‘(x) ‘((+ x x)) GE) (l-apply ‘(procedure ‘(x) ‘(+ x x) GE) ‘((+1 1 )) GE) (l-eval ‘(+ x x) E1) x : (‘thunk ‘(+1 1) GE) GE E1 (l-apply (actual-value ‘+ E1) ‘(x x) E1) (l-apply ‘(primitive #[add]) ‘(x x) E1) (apply-primitive-procedure ‘(primitive #[add]) (actual-value x E1)(actual-value x E1) ) (force-it (l-eval x E1)) (force-it (‘thunk ‘(+ 1 1) GE)) (actual-value ‘(+ 1 1) GE) (force-it (l-eval ‘(+ 1 1) GE)) ==> 2

95 94 (l-eval ‘(define f (lambda (x y) x)) GE) (eval-definition ‘(define f (lambda (x y) x)) GE) (define-variable! ‘f (l-eval ‘(lambda (x y) x) GE) GE) f: ‘(procedure ‘(x y) ‘(x) GE) GE

96 95 (l-eval ‘(f 1 1) GE) (l-apply (actual-value ‘f GE) ‘(1 1) GE) (force-it (l-eval ‘f GE)) ==> ‘(procedure ‘(x y) ‘(x) GE) (l-apply ‘(procedure ‘(x y) ‘(x) GE) ‘(1 1) GE) (l-eval ‘x E1) x : (‘thunk 1 GE) y: (‘thunk 1 GE) GE E1 ==> (‘thunk 1 GE)

97 96 (l-eval ‘(f (f 1 1) 1) GE) (l-apply (actual-value ‘f GE) ‘((f 1 1) 1) GE) (force-it (l-eval ‘f GE)) ‘(procedure ‘(x y) ‘(x) GE) (l-apply ‘(procedure ‘(x y) ‘(x) GE) ‘((f 1 1) 1) GE) (l-eval ‘x E1) x : (‘thunk ‘(f 1 1) GE) y: (‘thunk 1 GE) GE E1 ==> (‘thunk ‘(f 1 1) GE) Lets force this thunk..

98 97 (force-it (‘thunk ‘(f 1 1) GE)) (actual-value ‘(f 1 1) GE) (force-it (l-eval ‘(f 1 1) GE)) (force-it (l-apply (actual-value ‘f GE) ‘(1 1) GE)) (force-it (‘thunk 1 GE)) (actual-value 1 GE) (force-it (l-eval 1 GE)) ==> 1

99 98 Memo-izing Thunks Idea: once thunk exp has been evaluated, remember it If value is needed again, just return it rather than recompute thunk envexp Concretely – mutate a thunk into an evaluated-thunk evaluated- thunk result

100 99 Thunks – Memoizing Implementation (define (evaluated-thunk? obj) (tagged-list? obj 'evaluated-thunk)) (define (thunk-value evaluated-thunk) (cadr evaluated-thunk)) (define (force-it obj) (cond ((thunk? obj) (let ((result (actual-value (thunk-exp obj) (thunk-env obj)))) (set-car! obj 'evaluated-thunk) (set-car! (cdr obj) result) (set-cdr! (cdr obj) '()) result)) ((evaluated-thunk? obj) (thunk-value obj)) (else obj)))

101 100 Laziness and Language Design We have a dilemma with lazy evaluation Advantage: only do work when value actually needed Disadvantages –not sure when expression will be evaluated; can be very big issue in a language with side effects –may evaluate same expression more than once Alternative approach: give programmer control! Memoization doesn't fully resolve our dilemma Advantage: Evaluate expression at most once Disadvantage: What if we want evaluation on each use?

102 101 Variable Declarations: lazy and lazy-memo We want to extend the language as follows: (lambda (a (b lazy) c (d lazy-memo))...) "a", "c" are strict variables (evaluated before procedure application "b" is lazy; it gets (re)-evaluated each time its value is actually needed "d" is lazy-memo; it gets evaluated the first time its value is needed, and then that value is returned again any other time it is needed again.

103 102 Controllably Memo-izing Thunks thunk – never gets memoized thunk-memo – first eval is remembered evaluated-thunk – memoized-thunk that has already been evaluated when forced Thunk Memo envexp evaluated- thunk result

104 103 A new version of delay-it Look at the variable declaration to do the right thing... (define (delay-it decl exp env) (cond ((not (declaration? decl)) (l-eval exp env)) ((lazy? decl) (list 'thunk exp env)) ((memo? decl) (list 'thunk-memo exp env)) (else (error "unknown declaration:" decl))))

105 104 Change to force-it (define (force-it obj) (cond ((thunk? obj) ;eval, but don't remember it (actual-value (thunk-exp obj) (thunk-env obj))) ((memoized-thunk? obj) ;eval and remember (let ((result (actual-value (thunk-exp obj) (thunk-env obj)))) (set-car! obj 'evaluated-thunk) (set-car! (cdr obj) result) (set-cdr! (cdr obj) '()) result)) ((evaluated-thunk? obj) (thunk-value obj)) (else obj)))

106 105 Changes to l-apply Key: in l-apply, only delay "lazy" or "lazy-memo" params make thunks for "lazy" parameters make memoized-thunks for "lazy-memo" parameters (define (l-apply procedure arguments env) (cond ((primitive-procedure? procedure)...) ; as before; apply on list-of-arg-values ((compound-procedure? procedure) (l-eval-sequence (procedure-body procedure) (let ((params (procedure-parameters procedure))) (extend-environment (map parameter-name params) (list-of-delayed-args params arguments env) (procedure-environment procedure))))) (else (error "Unknown proc" procedure))))

107 106 Deciding when to evaluate an argument... Process each variable declaration together with application subexpressions – delay as necessary: (define (list-of-delayed-args var-decls exps env) (if (no-operands? exps) '() (cons (delay-it (first-variable var-decls) (first-operand exps) env) (list-of-delayed-args (rest-variables var-decls) (rest-operands exps) env))))

108 107 Syntax Extensions – Parameter Declarations (define (first-variable var-decls) (car var-decls)) (define (rest-variables var-decls) (cdr var-decls)) (define declaration? pair?) (define (parameter-name var-decl) (if (pair? var-decl) (car var-decl) var-decl)) (define (lazy? var-decl) (and (pair? var-decl) (eq? 'lazy (cadr var-decl)))) (define (memo? var-decl) (and (pair? var-decl) (eq? 'lazy-memo (cadr var-decl))))

109 108 Summary Lazy evaluation – control over evaluation models Convert entire language to normal order Upward compatible extension –lazy & lazy-memo parameter declarations

110 109 How do we use this new lazy evaluation? Our users could implement a stream abstraction: (define (cons-stream x (y lazy-memo)) (lambda (msg) (cond ((eq? msg 'stream-car) x) ((eq? msg 'stream-cdr) y) (else (error "unknown stream msg" msg))))) (define (stream-car s) (s 'stream-car)) (define (stream-cdr s) (s 'stream-cdr))

111 110 Go over this example at home: infinite streams We want to work with non-strict cons, car, cdr. We work with our lazy evaluator Running over the non-lazy Scheme. (define (cons x y) (lambda (m) (m x y))) (define (car z) (z (lambda (p q) p))) (define (cdr z) (z (lambda (p q) q))) We define:

112 111 Our example; the ones stream. (define ones (cons 1 ones)) (car ones) We will do it in detail. We will be so tired after that, that we won’t need more examples… And we will also be running out of time…

113 112 So we start… The outer loop gets the input: exp= “(define ones (cons 1 ones))” It calls: (actual-value ‘(define ones (cons 1 ones)) GE) It calls: (force-it (l-eval ‘(define ones (cons 1 ones)) GE)) The actual-value from the outer loop, is the one sending the force, which wakes up all the sleeping values that are needed.

114 113 It’s a definition, so… (force-it (eval-definition ‘(define ones (cons 1 ones)) GE)) (force-it (define-variable! (definition-variable ‘(define..)) (l-eval (definition-value ‘(define..)) GE) GE)) (force-it (define-variable! ‘ones (l-eval ‘(cons 1 ones) GE) GE))

115 114 Eval again, and it’s … a procedure (force-it (define-variable! ‘ones (l-apply (actual-value ‘cons GE) ‘(1 ones) GE)) (actual-value ‘cons GE) (force-it (l-eval ‘cons GE)) (force-it #proc-..) #proc-.. (force-it (define-variable! ‘ones (l-apply #proc-cons ‘(1 ones) GE)) GE))

116 115 L-apply gets into action… (force-it (define-variable! ‘ones (l-eval-sequence ((lambda (m) (m x y))) (extend-environment ‘(x y) (list-of-delayed-args ‘(1 ones) GE) GE))

117 116 The whole point: the arguments are delayed. (force-it (define-variable! ‘ones (l-eval-sequence ((lambda (m) (m x y))) (extend-environment ‘(x y) (list (delay-it ‘1 GE) (delay-it ‘ones GE)) GE))

118 117 I.e., the arguments are thunks. (force-it (define-variable! ‘ones (l-eval (lambda (m) (m x y)) (extend-environment ‘(x y) (list (list 'thunk ‘1 GE) (list ‘thunk ‘ones GE)) GE))

119 118 L-eval again. This time.. A procedure (force-it (define-variable! ‘ones (make-procedure (lambda-parameters ‘(lambda (m) (m x y))) (lambda-body ‘(lambda (m) m x y))) (extend-environment ‘(x y) (list (list 'thunk ‘1 GE) (list ‘thunk ‘ones GE)) GE))

120 119 So.. We create the new environment We make the procedure We assign the procedure value to the variable ‘ones in GE define-variable! Returns ‘ok It’s not a thunk so force-it returns it as well. GE E1 x: (list 'thunk ‘1 GE) y: (list ‘thunk ‘ones GE)) ones: p: m body: (m x y)

121 120 Now we try ‘(car ones) (actual-value ‘(car ones) GE) (force-it (l-eval ‘(car ones) GE)) (force-it (l-apply #proc-car ‘ones GE)) (force-it (l-eval-sequence ‘((z (lambda (p q) p))) (extend-environment ‘(z) (list-of-delayed-args ‘ones GE) GE)))

122 121 L-eval gets ready.. (force-it (l-eval ‘(z (lambda (p q) p)) (extend-environment ‘(z) (list (list 'thunk ‘ones GE)) GE))) l-eval looks at the expression, and it’s a compound procedure.

123 122 L-eval moves (force-it (l-eval (l-apply (actual-value ‘z (extend-env..)) ‘(lambda (p q) p) (extend-environment ‘(z) (list (list 'thunk ‘ones GE)) GE)))

124 123 (actual-value ‘z (extend-env..)) Actual-value opens to: (force-it (l-eval ‘z (extend-environment ‘(z) (list (list 'thunk ‘ones GE)) GE))) (force-it (list ‘thunk ‘ones GE)) (actual-value ‘ones GE) It is NOT a thunk, so we get: ones the address of The procedure ones.

125 124 Back to L-eval (force-it (l-eval (l-apply ones ‘(lambda (p q) p) (extend-environment …))) We move the ball to l-apply…

126 125 L-eval again.. (force-it (l-eval (l-eval ‘(m x y) (extend-environment ‘(m) (list-of-delayed-args ‘(lambda (p q) p) (extend-env…)) (extend-environment ‘(x y) (list (list 'thunk ‘1 GE) (list ‘thunk ‘ones GE)) GE)))))) E2

127 126 And l-eval again… L-eval gets into action. It calls: (l-apply (actual-value (operator exp) E2) … This forces m, and assigns m the procedure (lambda (p q) p) At this stage x,y are still delayed. We now get: (l-apply (lambda (p q) p) ‘(x y) E2)

128 127 And l-apply again… (l-eval ‘p (extend-environment ‘(p q) (list-of-delayed-args ‘(x y) E2) E2)) In the extended environment E2, p gets bounded to (list ‘thunk x E1) And so this is what l-eval returns. (list ‘thunk x E1)

129 128 We know force it. Obj = (list ‘thunk x E1) (actual-value (thunk-exp obj) (thunk-env obj))) (actual-value x E1) (force-it (l-eval x E1)) (force-it (list ‘thunk ‘1 GE)) ‘1 We had to force the object twice!

130 129 Add-lists and the integers Now consider: (define (add-lists list1 list2) (cond ((null? List1) list2) ((null? List2) list1) (else (cons (+ (car list1) (car list2)) (add-lists (cdr list1) (cdr list2)))))) (define ones (cons 1 ones)) (define integers (cons 1 (add-lists one integers))


Download ppt "1 Lecture 19 Review last lecture on evaluator, Dynamic Scoping Lazy Evaluation (4.2.1, 4.2.2)"

Similar presentations


Ads by Google