400 likes | 490 Views
Assignments and Procs w/ Params. Assignment, LHS := RHS. l-value: left-, location, address, reference, … r-value: right-, int , real, address, … env and store allow one to describe the semantics of assignment in a purely functional style. (DENOTATIONAL SEMANTICS)
E N D
Assignment, LHS := RHS • l-value: left-, location, address, reference, … • r-value: right-, int, real, address, … • env and store allow one to describe the semantics of assignment in a purely functional style. (DENOTATIONAL SEMANTICS) • Object language structures are mapped to similar Scheme structures. (META-CIRCULAR INTERPRETER)
Sharing • sharing/aliasing • Point p = • new Point(); • Point q = p; • call by reference • void f(Point p){…}; • f(q);
Side-effects in Scheme • Update variable: (set! var exp) • denotes location denotes value • In Scheme locations are denotable, but not expressible • Sequencing: • (begin exp1 exp2 … expn) • Ordering of expressions important
Expressible vs. Denotable values • Expressible Values • the language can express and compute these • represented in the language's syntax as expressions • Denotable Values • represented in syntax by declarations • the names and their values saved within a data structure (called a namespace or environment or symbol table or activation record) • In some languages, these two are not equal. SchemeRefresher
Expressible vs. Denotable values • Booleans are expressible in (early) FORTRAN, but not denotable. • Functions are denotable in many languages, but are not expressible. • In (functional subset of) Scheme, both value spaces are identical. • In (full) Scheme, variable references (pointers) are denotable but not expressible. SchemeRefresher
Local Binding: let • (letproc-id ([idinit-expr] ...) body ...+) • Defines a local procedure. Evaluates the init-exprs; these become arguments to the proc. The ids must be distinct. • (let fac ([n 10]) (if (zero? n) 1 (* n (fac (sub1 n))))) 3628800 SchemeRefresher
Local Binding: let* • (let* ([idval-expr] ...) body ...+) • Similar to let, but evaluates the val-exprs one by one, creating a location for each id as soon as the value is available. The ids are bound in the remainingval-exprs as well as the bodys, and the ids need not be distinct; later bindings shadow earlier bindings. • (let* ([x 1] • [y (+ x 1)]) • (list y x)) (2 1) SchemeRefresher
Local Binding: letrec • (letrec ([idval-expr] ...) body ...+) • Similar to let, but the locations for all ids are created first and filled with #<undefined>, and all ids are bound in all val-exprs as well as the bodys. The ids must be distinct. • (letrec ((a b) (b 34) (c (+ b 5))) • (list a b c)) (#<undefined> 34 39) • (letrec ([is-even? (lambda (n) • (or (zero? n) • (is-odd? (sub1 n))))] • [is-odd? (lambda (n) • (and (not (zero? n)) • (is-even? (sub1 n))))]) • (is-odd? 11)) #t SchemeRefresher
Comparison: let, let*, letrec • (let/let*/letrec ((v1 e1 ) (v2 e2 ) … (vn en )) body ) • let • no vi is created until all ei are evaluated. • none of ei can refer to any vi • let* • e1 is evaluated; v1 created, bound to e1; • e2 is evaluated; v2 created, bound to e2; …; • ej can refer to earlier vi, i < j. • letrec • vi are created with #undefined as their value. • with the above in effect, e1, …, en are evaluated l-to-r • each vi is now bound to ei SchemeRefresher
Simulating Scheme letrec • (letrec ((v1 exp1) (v2 exp2)) exp) • ;; exp1 and exp2 are lambda-forms • (let ((v1 ’*) (v2 ’*)) • (set! v1 exp1) • (set! v2 exp2) • exp )
Env and Store • For functional subset, it is sufficient to model env as a function from ids to values. • For imperative programming that has both assignment and sharing, separating env and store is necessary. • env: identifier location • store: location value • assignment: location x value x store store
Our assignment, set var = exp • Concrete Syntax: set identifier = expression • AST: varassign-exp (id rhs-exp) • Semantics: add a new variant to our data type for expressions (varassign-exp (id symbol?) (rhs-exp expression?))
Ex1 Code in Our PL let x = 0 in letrec even () = if zero? (x) then 1 else let d = set x = sub1(x) in (odd) odd () = if zero? (x) then 0 else let d = set x = sub1(x) in (even) in let d = set x = 13 in (odd)
Ex2 Code in Our PL let g = let count = 0 in proc () let d = set count = add1(count) in count in +((g), (g)) • Evaluates to 3
Ex3 Code in Our PL let x = 100 in let p = proc (x) let d = set x = add1(x) in x in +((p x),(p x)) • Evals to 202
implement assignment (define-datatype reference reference? (a-ref (position integer?) (vec vector?)))
Deref and setref! (define primitive-deref (lambda (ref) (cases reference ref (a-ref (pos vec) (vector-ref vec pos))))) (define primitive-setref! (lambda (ref val) (cases reference ref (a-ref (pos vec) (vector-set! vec pos val))))) (define deref (lambda (ref) (primitive-deref ref))) (define setref! (lambda (ref val) (primitive-setref! ref val)))
apply-env and apply-env-ref (define apply-env (lambda (env sym) (deref (apply-env-ref env sym)))) (define apply-env-ref (lambda (env sym) (cases environment env (empty-env-record () (eopl:error 'apply-env-ref "No binding for ~s" sym)) (extended-env-record (symsvalsenv) (let ((pos (rib-find-position sym syms))) (if (number? pos) (a-ref pos vals) (apply-env-ref env sym)))))))
varassign-exp (varassign-exp (id rhs-exp) (begin (setref! (apply-env-ref env id) (eval-expression rhs-exp env)) 1))
call-by-value v. -by-ref • Under call-by-value, a new reference is created for every evaluation of an operand • Under call-by-reference, a new reference is created for every evaluation of an operand other than a variable.
Call by-val v. -by-ref let a = 3 p = proc (x) set x = 4 in begin (p a); a end • Call by val: 3 • Call by ref: 4
call-by-reference example let a = 3 b = 4 swap = proc (x, y) let temp = x in begin set x = y; set y = temp end in begin (swap a b); -(a, b) end By-ref, evals to +1. By-val, evals to -1.
Implementation of call by-ref • define-datatype target • direct targets • expressed values • corresponds to the behavior of call-by-value • indirect targets • references to expressed values • corresponds to the behavior of call-by-ref • no new location is created
datatype target (define-datatype target target? (direct-target (expvalexpval?)) (indirect-target (ref ref-to-direct-target?))) (define expval? (lambda (x) (or (number? x) (procval? x))))
direct-target (define ref-to-direct-target? (lambda (x) (and (reference? x) (cases reference x (a-ref (pos vec) (cases target (vector-ref vec pos) (direct-target (v) #t) (indirect-target (v) #f)))))))
deref (define deref (lambda (ref) (cases target (primitive-deref ref) (direct-target (expval) expval) (indirect-target (ref1) (cases target (primitive-deref ref1) (direct-target (expval) expval) (indirect-target (p) (eopl:error 'deref "Illegal reference: ~s" ref1)))))))
setref! (define setref! (lambda (ref expval) (let ((ref (cases target (primitive-deref ref) (direct-target (expval1) ref) (indirect-target (ref1) ref1)))) (primitive-setref! ref (direct-target expval)))))
Env built by call-by-reference (proc (t, u, v, w) % p1 (proc (a, b) % p2 (proc (x, y, z) % p3 set y = 13 a b 6) 3 v) 5 6 7 8)
Lazy Evaluation • Sometimes in a given call a procedure never refers to one or more of its formal parameters. • A thunk is a procedure with no arguments. • One can delay (perhaps indefinitely) the evaluation of an operand by encapsulating it as a thunk. • Freezing: forming thunks • Thawing: evaluating thunks
call-by-name, -by-need • call-by-name: invoke the thunk every time the parameter is referred to. • In the absence of side effects this is a waste of time, since the same value is returned each time. • call-by-need: record the value of each thunk the first time it is invoked, andthereafter refers to the saved value. • an example of memoization.
Example Blocks and Stmts varx,y; {x = 3; y = 4; print (+ (x,y))} varx,y,z; {x = 3; y = 4; z = 0; while x do {z = + (z, y); x = sub1(x)}; print (z)} var x; {x = 3; print (x); var x; {x = 4; print (x)}; print (x)} var f, x; {f = proc (x, y) * (x, y); x = 3; print ((f 4 x))}
execute-program (define execute-program (lambda (pgm) (cases program pgm (a-program (stmt) (execute-stmt stmt (init-env))))))
execute-stmt (define execute-stmt (lambda (stmt env) (cases stmt stmt (assign-stmt (id exp) …) (print-stmt (exp) … ) (compound-stmt (stmts) …) (if-stmt (exp true-stmt false- stmt) …) (while-stmt (exp stmt) …) (block-stmt (ids stmt) … ))))
…, while-stmt, … • (compound-stmt (stmts) (for-each (lambda (stmt) (execute-ststenv)) sts)) • (while-stmt (exp stmt) (let loop () (if (true-value? (eval-expression exp env)) (begin (execute-stmt stmt env) (loop))))) • (block-stmt (ids stmt) (execute-stmt stmt (extend-env ids idsenv)))