Presentation is loading. Please wait.

Presentation is loading. Please wait.

Principles of Programming Languages

Similar presentations


Presentation on theme: "Principles of Programming Languages"— Presentation transcript:

1 Principles of Programming Languages
Slides by Dana Fisman and Yaron Gonen based on lectures notes by Michael Elhadad and book by Mira Balaban Lesson 12 – Environment Model of Operational Semantics

2 The Substitution Model
The substitution model for operational semantics involves the following operations: Argument evaluation Renaming Substitution Reduction Disadvantages: Procedure body is repeatedly evaluated In every procedure application body is renamed The AST is copied for renaming, substitute and reduce. The substitution interpreter we reviewed is so slow that it is barely usable.

3 The environment Interpreter
The environment model for operational semantics aims at optimizing the substitution model into an efficient procedure execution algorithm. The idea: Replace substitution and renaming by a data structure – the environment The environment is enhanced with new information when proc-exp is evaluated (closure creation) and accessed on procedure applications

4 applic-eval vs. env-eval
The environment model for operational semantics aims at optimizing the substitution model (applicative-eval alg.) into an efficient procedure execution algorithm. The two models have the same semantics but env-eval is much faster 

5 The Gist of appl-eval / env-eval
On procedure application: Compute the values of the arguments Rename bound variables in the body Replace all free occurrences of type var-ref of the parameters of the body with the computed values. Objective: On reduce (evaluation of body) var-ref x should be replaced by its correct value The env-eval interpreter uses a lazy approach: Instead of replacing var-refs before reduction Reduction takes place with a substitution object on the side

6 Data Structures of the environment interpreter
The closure data structure is modified to carry an environment (recording the values of variables in creation) Interface Closure { params : List(symbols) , body : List(cexp), env : List(binding) } (define y 7) (define z 3) (define foo (lambda (x) (* x y))) Closure-Params: (x) Closure-Body: (* x x) Closure-Env: { y=7, z=1 }

7 of the environment interpreter
Data Structures of the environment interpreter The environment data structure is organized as a linked list of frames - each frame maps variables to values. The links in the list of frames correspond to nested invocations of closures.

8 Evaluation Rules of the environment interpreter
Expressions are evaluated with respect to an environment. The environment plays the role of a context for the evaluation – (* x x) in {x=2} evaluates to 4 (* x x) in {x=3} evaluates to 9 The evaluation rule for procedure application is modified, so to replace substitution (and renaming) by evaluation in the environment.

9 Environment Terminology
Environment structure: A binding is a variable-value. A frame is a finite sequence of bindings. An environment is a finite sequence of frames: <f1; f2; ... ; fn>. The empty sequence of frames is called the empty environment. Environment relations: An environment <f1; f2; ... ; fn> includes n embedded environments: <f1; f2; ... ; fn>, <f2; ... ; fn>, … , <fn>. The environment <fi+1; fi+2; ... ; fn> is the enclosing environment of the frame fi in <f1; f2; ... ; fn> The frame fi extends the environment <fi+1; fi+2; ... ; fn> in <fi; fi+1; ... ; fn> The environments <k; f1; f2; ... ; fn> and <l; f1; f2; ... ; fn> are said to be tail-sharing.

10 Variable-Value Definitions
Assume an env E = < f1, f2, f3 > with frames f1 = {x1=v1, x2=v2} f2 = {y1=u1, y2=u2, y3=u3 } f3 = {y1=w1} apply-frame(f2,y2)= apply-frame(f2,x2) apply-env(E,y1)= apply-env(E,x3)= u2 =unbound u1 unbound The value of x in frame f is denoted f(x) and in the code apply-frame(f,x) The value of x in env E is denoted E(x) and in the code apply-env(E,x) It is the value of x in the first frame of E in which it is defined (if such exists) Otherwise it is unbound in E.

11 Frame and Env Interface
Value constructors: Frame: make-frame(vars,vals) we will denote frames as {var=val, ...} Environment: make-empty-env() extend-env(env, frame) Accessors: apply-frame(frame, var) which we will denote frame(var) apply-env(env, var) which we will denote env(var) Env-accessors : env->frame(env) and env->enclosing-env(env)

12 Evaluating Let-expressions
We extend L3 with let expressions: <let-exp> ::= (let (<binding>*) <cexp>+) <binding> ::= (<var> <cexp>) // let-exp(bindings:List(Binding), body:List(Cexp)) // binding(var:Var-decl, val:Cexp) (let ([x 1] [y 2]) (+ x y)) We need to have the values of x and y available when we compute the body. We thus first create the environment then evaluate compute((+ a b), env({a=1, b=2}))

13 Evaluating Let-expressions
(let ([a 1]) (let ([b (+ a a)]) (+ a b))) compute((+ a a), env({a=1})) compute((+ a b), env({b=2})) compute((+ a b), env({a=1,b=2}))

14 Let Evaluation Rule Assume exp = let-exp(bindings, body):
env-eval(exp, env) is computed by: let vars = variables in bindings val-exprs = value expressions in bindings let cvals = eval(val-exp, env) for val-exp in val-exprs return eval-sequence(body, extend-env(env,make-frame(vars, cvals))) Thus (let ([a 1] [b (+ a 1)] … ) won’t work!

15 From Let to App-Proc Application-Expr Evaluation Rule – Attempt:
(let ([a 1] [b 2]) <body>) ~ ((lambda (a b) <body>) 1 2) Application-Expr Evaluation Rule – Attempt: Assume exp = app-exp(rator, rands): env-eval(exp, env) is computed by: let proc = env-eval(rator, env) args = eval(rand, env) for rand in rands if proc is a closure: let params = closure->params(proc) body = closure->body(proc) return eval-sequence(body, extend-env(env, make-frame(params, args))) else return apply-primitive(proc, args)

16 From Let to Closure ((lambda (a b) (+ a b)) 1 2)

17 The closure also needs to remember binding of free vars
From Let to Closure (let ([x 1]) ;; E1 (let ([p (lambda (y) (+ x y))]) E2 (let ([x 2]) E3 (p x)))) Each let evaluation extends the env: Following rule for app-proc in E3: The closure also needs to remember binding of free vars Result is 4. But should be 3.

18 Fixing the problem We update the definition of closure to include
The closure also needs to remember binding of free vars We update the definition of closure to include Params Body Environment at closure creation time <closure> ::= closure(params:List(Var-decl), body:List(CExp), env:Env) Modify the app-proc rule to use the env of the closure instead of the current-env

19 Correct Evaluation Rule
If exp=proc-exp(params, body): env-eval(exp, env) is computed by: return make-closure(params, body, env) If exp=app-exp(rator, rands): env-eval(exp, env) is computed by: let proc = env-eval(rator, env) args = eval(rand, env) for rand in rands if proc is a closure: let params = closure->params(proc) body = closure->body(proc) closure-env = closure->env(proc) return eval-sequence(body, extend-env(closure-env, make-frame(params, args))) else return apply-primitive(proc, args)

20 Correct Evaluation Rule
(let ([x 1]) (let ([p (lambda (y) (+ x y))]) (let ([x 2]) (p x)))) ;; E1 ;; E2 ;; E3 Store env Use stored env

21 Visual Representation of Envs

22 Visual Representation of Closures

23 Example P1: P3: B3: (define sq (lambda (x) B1: (* x x)))
(define sum-of-square (lambda (x y) :P2 B2: (+ (sq x) (sq y)))) (define f P3: (lambda (a) B3: (sum-of-squares (+ a 1) (* a 2)))) (f 5) GE sq: sum-of-squares: f: P1: (x) B1: (* x x) p2: (x y) B2: (+ (sq x) (sq y)) P3: (a) B3: (sum-of-squares … )

24 Example (define sq (lambda (x) (* x x)))
(define sum-of-square (lambda (x y) (+ (sq x) (sq y)))) (define f (lambda (a) (sum-of-squares (+ a 1) (* a 2)))) (f 5) GE sq: sum-of-squares: f: P1 : (x) B1: (* x x) P2 : (x y) B2: (+ (sq x) (sq y)) P1 : (a) B3: (sum-of-squares … ) E1 B GE 3 a:5

25 Example (define sq (lambda (x) (* x x)))
(define sum-of-square (lambda (x y) (+ (sq x) (sq y)))) (define f (lambda (a) (sum-of-squares (+ a 1) (* a 2)))) (f 5) GE sq: sum-of-squares: f: P1 : (x) B1: (* x x) P2 : (x y) B2: (+ (sq x) (sq y)) P1 : (a) B3: (sum-of-squares … ) E1 B GE 3 a:5 E2 B2 E1 x:6 y:10

26 Example (define sq (lambda (x) (* x x)))
(define sum-of-square (lambda (x y) (+ (sq x) (sq y)))) (define f (lambda (a) (sum-of-squares (+ a 1) (* a 2)))) (f 5) GE sq: sum-of-squares: f: P1 : (x) B1: (* x x) P2 : (x y) B2: (+ (sq x) (sq y)) P1 : (a) B3: (sum-of-squares … ) E1 B GE 3 a:5 E2 x:6 y:10 B2 E1 E3 B1 E2 x:6

27 Example (define sq (lambda (x) (* x x)))
(define sum-of-square (lambda (x y) (+ (sq x) (sq y)))) (define f (lambda (a) (sum-of-squares (+ a 1) (* a 2)))) (f 5) GE sq: sum-of-squares: f: P1 : (x) B1: (* x x) P2 : (x y) B2: (+ (sq x) (sq y)) P1 : (a) B3: (sum-of-squares … ) E1 B GE 3 a:5 E2 x:6 y:10 B2 E1 E3 x:6 B1 E2 E 4 B1 E2 x:10

28 Example (define sq (lambda (x) (* x x)))
(define sum-of-square (lambda (x y) (+ (sq x) (sq y)))) (define f (lambda (a) (sum-of-squares (+ a 1) (* a 2)))) (f 5) GE sq: sum-of-squares: f: P1 : (x) B1: (* x x) P2 : (x y) B2: (+ (sq x) (sq y)) P1 : (a) B3: (sum-of-squares … ) E1 B GE 3 a:5 136 E2 x:6 y:10 B2 E1 136 E3 x:6 B1 E2 36 E 4 B1 E2 100 x:10

29 Using Closures The environment model refined our understanding of closures. Closures capture a reference binding in the environment they are created (“early binding”). Let’s see some programming techniques that exploit this property.

30 Frames Lifecycle in the presence of closures
(let ([a 1]) (lambda (x) (+ x a))) Frame E1: <{a=1}> created Frame E1 discarded #<procedure> (let ([f (let ([a 1]) Frame E1: <{a=1}> created (lambda (x) (+ x a)))])Frame E2: <{f=(closure (x) (+x a)) ; E1}> created Frame E1 discarded (f 10)) 11 Frame E1: <{a=1}> created (let ([f (let ([a 1]) Frame E2: <{f=(closure (x) (+x a)) ; E1}> (lambda (x) (+ x a)))]) created a) a: undefined Frame E1 discarded

31 Frames Lifecycle in the presence of closures Conclusion: (let ([a 1])
(lambda (x) (+ x a))) #<procedure> Conclusion: (let ([f (let ([a 1]) (lambda (x) (+ x a)))]) (f 10)) The lifecycle of frames in the presence of closures must take into account the references that closures keep to frames. 11 (let ([f (let ([a 1]) (lambda (x) (+ x a)))]) a) a: undefined

32 Closure Factories The environment memory in closures can be used to create objects with private memory (define make-adder (lambda (a) ;; Parameters to the closure maker (lambda (x) ;; Construction of the closure (+ a x)))) ;; Body of the closure - refers to the ;; private environment of the closure (define a3 (make-adder 3)) ;; This adder has a private state a=3 (define a5 (make-adder 5)) ;; a5 has a private state a=5 (a3 2) 5 7 3 (a5 2) ((make-adder 1) 2)

33 Implementing Compound Data Structures using Closures
Apparently cons, car, and cdr do not need to be language primitives – we can implement them ourselves, using closures! Same is true for more complex data structures such as class objects! How?

34 Implementing Compound Data Structures using Closures
;; Purpose: closure-based pair value constructor ;; Signature: make-pair(a,b) ;; Type: [T1 * T2 -> Closure-Pair(T1,T2)] (define make-pair (lambda (a b) ;; Parameters of the pair constructor (lambda (cond (msg) [(eq? ;; Interface of the pair object msg 'car) a] ;; Dispatch method for the pair object msg 'cdr) b])))) What if we want to compute something on a and b? Add an additional type of msg for that (let ([p1 (make-pair 1 2)] [p2 (make-pair 3 4)]) (+ (p1 'car) (p2 'cdr))) 5

35 Implementing Compound Data Structures using Closures
;; Purpose: closure-based pair value constructor ;; Signature: make-adder-pair(a,b) ;; Type: [Number * Number-> Adder-Pair] (define make-adder-pair (lambda (a b) ;; Parameters of the adder-pair constructor (lambda (msg) ;; Interface of the adder-pair object (cond [(eq? msg 'car) a] ;; Dispatch method for the object [(eq? msg 'cdr) b] [(eq? msg 'add) (+ a b)])))) (let ([p1 (make-adder-pair 1 2)] [p2 (make-adder-pair 3 4)]) What if the compute (+ (p1 'add) (p2 'add))) function needs a 10 parameter?

36 Implementing Compound Data Structures using Closures
;; Purpose: closure-based pair value constructor ;; Signature: make-scale-pair(a,b) ;; Type: [Number * Number-> Scale-Pair] (define make-scale-pair (lambda (a b) ;; Parameters of the scale-pair constructor (lambda (msg) ;; Interface of the scale-pair object (cond [(eq? msg 'car) a] ;; Dispatch method for the object [(eq? msg 'cdr) b] [(eq? msg 'scale) (lambda (k) (make-scale-pair (* k a) (* k b)))] )))) We can generalize the access pattern to the fields (let ([p1 (make-scale-pair 1 2)]) (let ([kp1 ((p1 'scale) 3)]) (kp1 'cdr))) of a closure-based compound data structure by accepting a functional 6 parameter.

37 Implementing Compound Data Structures using Closures - Generalization
;; Purpose: value constructor for pair-sel data structure ;; Signature: make-pair-sel(a,b) ;; Type: [T1 * T2 -> Pair-Sel(T1,T2) (define make-pair-sel (lambda (a b) ;; parameters to the constructor (lambda (sel) ;; closure constructor General selector A function (sel a b)))) ;; body of the closure - application! parameter ;; Purpose: access the first element in a pair-sel ;; Signature: pair-first(pair-sel) ;; Type: [Pair-sel(T1, T2) -> T1] (define pair-first (lambda (pair) (pair (lambda (a b) a)))) ;; Pass the a selector ;; Purpose: access the second element in a pair-sel ;; Signature: pair-second(pair-sel) ;; Type: [Pair-sel(T1, T2) -> T2] (define pair-second (lambda (pair) (pair (lambda (a b) b))))

38 Implementing Compound Data Structures using Closures - Generalization
(define make-pair-sel (lambda (a b) ;; parameters to the constructor (lambda (sel) ;; closure constructor (sel a b)))) ;; body of the closure – (define pair-first (lambda (pair) (pair (a b) a)))) (let ([p1 (make-pair-sel 1 2)] [p2 (make-pair-sel 3 4)]) (+ (pair-first p1) (pair-second p2))) 5 (let ([p3 (make-pair-sel 5 6)]) (p3 (lambda (a b) (+ a b)))) 11

39 Implementing Compound Data Structures using Closures - Generalization
(define make-pair-sel (lambda (a b) ;; parameters to the constructor (lambda (sel) ;; closure constructor (sel a b)))) ;; body of the closure - ;; Scale pair - as above, the selector constructs a closure method ;; which accepts the k parameter. (let ([p4 (make-pair-sel 7 8)]) (let ([p5-scale (p4 (lambda (a b) (lambda (k) (make-pair-sel (* k a) (* k b)))))]) (pair-first (p5-scale 2)))) 14

40 Implementing Compound Data Structures using Closures - Generalization
(define make-pair-sel (lambda (x y) (lambda (sel) B1 (sel x y)))) B2 (define p1 (make-pair-sel 5 10)) GE make-pair-sel: p1: p:(x y) b1:(lambda(sel)…) E1 b1 GE x:5 y:10 p:(sel) b2:(sel x y)

41 Summary The environment model is an optimization of the substitution applicative model of the operational semantics. The optimization consists of avoiding the cost of substitution and renaming when applying closures to their arguments Instead delay the evaluation of var-refs Record the binding needed for evaluation in the environment data structure The environment structure is made up of linked list of frames. Each frame contains bindings of variables to values

42 Summary When a closure is constructed, a new field in the closure remembers the environment that was active when the closure was created. When a closure is applied to arguments, the body of the closure is evaluated in the environment which extends the closure env with the frame mapping the parameters of the closure to the values of the arguments. In dynamic scoping closures do not remember their environment, and when they are applied, they are applied in the current environment. This is a different, unattractive, method to maintain the environment structure - which leads to a different operational semantics for a given language. Environment diagrams illustrate the structure of lexical scopes, the bindings of variables and the control order of environments creation.

43 Summary Closures can be used to remember bindings and maintain private state. This is obtained by a function returning another function, some of its variables are already bound. Compound data structures can be implemented using closures with a message dispatch pattern (make-pair example). Compound data structures can also be implemented using closures and a functional selector pattern (make-pair-sel example).


Download ppt "Principles of Programming Languages"

Similar presentations


Ads by Google