2
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.
• 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.
CS784(PM)
3
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)
CS784(PM)
4
Sharing
• sharing/aliasing• Point p = new Point();
• Point q = p;
• call by reference• void f(Point p){…};
• f(q);
CS784(PM)
5
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
CS784(PM)
Local Binding: let
• (let proc-id ([id init-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))))) 3628800CS784(PM) 6
Local Binding: let*
• (let* ([id val-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 remaining val-exprs as well as the bodys, and the ids need not be distinct; later bindings shadow earlier bindings.
1. (let* ([x 1]
2. [y (+ x 1)])
3. (list y x)) (2 1)
CS784(PM) 7
Local Binding: letrec
• (letrec ([id val-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.
1. (letrec ((a b) (b 34)(c (+ b 5)))
2. (list a b c)) (#<undefined> 34 39)
3. (letrec ([is-even? (lambda (n)
4. (or (zero? n)
5. (is-odd? (sub1 n))))]
6. [is-odd? (lambda (n)
7. (and (not (zero? n))
8. (is-even? (sub1 n))))])
9. (is-odd? 11)) #t
CS784(PM) 8
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
CS784(PM) 9
10
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 )
CS784(PM)
11
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
CS784(PM)
EXPLICIT-REFS
• ExpVal = Int + Bool + Proc + Ref(ExpVal)• DenVal = ExpVal
– Ref(ExpVal) == references to locations that contain expressed values.
• newref: allocates a new location, returns a ref to it.• deref: dereferences• setref: changes the contents• This gives a clear account of
– allocation, dereferencing, and mutation
CS784(PM) 12
Even and Odd Redone
let x = newref(0) in letreceven(dummy)=
if zero?(deref(x)) then 1else beginsetref(x, -(deref(x),1));(odd 888)end
odd(dummy)=if zero?(deref(x)) then 0else begin
setref(x, -(deref(x),1));(even 888)end
in begin setref(x,13); (odd 888) end
CS784(PM) 13
Hidden State
let g =let counter = newref(0)in proc (dummy)
beginsetref(counter,-(deref(counter),-1));deref(counter)end
in let a = (g 11)in let b = (g 11)
in -(a,b)
CS784(PM) 14
Store-Passing Specifications
• [c = v]σ location c is mapped to v in store σ• (value-of exp1 ρ σ0) = (val1, σ1)• specification for diff-exp
(value-of exp1 ρ σ0) = (val1, σ1) and
(value-of exp2 ρ σ1) = (val2, σ2)
implies(value-of (diff-exp exp1 exp2) ρ σ0)= ( [val1] – [val2], σ2)
(caution: [] incorrect symbols)
CS784(PM) 16
conditional
Let (value-of e1 ρ σ0) = (v1, σ1).
Then
(value-of (if-exp e1 e2 e3) ρ σ0)
= (value-of e2 ρ σ1) if (expval->bool v1) = #t
= (value-of e3 ρ σ1) if (expval->bool v1) = #f
CS784(PM) 17
newref, deref, and setref
Expression ::= newref (Expression)
AST: newref-exp (exp1)
Expression ::= deref (Expression)
AST: deref-exp (exp1)
Expression ::= setref (Expression, Expression)
AST: setref-exp (exp1 exp2)
CS784(PM) 18
Specs of newref
• Given: – (value-of exp ρ σ0) = (val, σ1), lc ! dom(∈ σ1 )
• (value-of (newref-exp exp) ρ σ0) = ((ref-val lc), [lc=val] σ1)
• newref-exp evaluates its operand. Allocates a new location lc and stores val in that location. Then it returns a reference to a location lc that is new. This means that the new loc is not already in the domain of σ1.
CS784(PM) 19
Specs of deref
• Given: (value-of exp ρ σ0) = (lc, σ1)• (value-of (deref-exp exp) ρ σ0) = (σ1(lc),
σ1)• exp evaluation leaves the store in state σ1.
The value of that argument should be a reference to a location lc. The deref-exp then returns the contents of lc in σ1 , without any further change to the store.
CS784(PM) 20
21
spec of setref
• Given:• (value-of exp1 ρ σ0) = (lc, σ1)• (value-of exp2 ρ σ1) = (val, σ2) ;; note σ1 σ2 order• Then:• (value-of (setref-exp exp1 exp2) ρ σ0)
= ( [23], [lc = val] σ2) ;; caution []
• setref-exp evaluates exp1 first, exp2 second. First value must be a reference to a location lc.
• setref-exp then updates σ2 by putting val in location lc. It• could return anything; e.g. 23. • This expression is executed for its effect, not its value.CS784(PM)
Implementation
• state σ of the store as a Scheme value– represent the store as a list of expressed
values,
• keep the state in a single global variable• all the procedures of the impl have access. • This representation is extremely inefficient. CS784(PM) 22
A naive model of the store 1/3
(define empty-store
(lambda () ’()))
(define the-store ’uninitialized) ; initially
(define get-store
(lambda () the-store))
(define initialize-store!
(lambda ()
(set! the-store (empty-store))))
CS784(PM) 23
A naive model of the store 2/3
(define reference?
(lambda (v)
(integer? v)))
(define newref
(lambda (val)
(let ((next-ref (length the-store)))
(set! the-store (append the-store (list val)))
next-ref)))
(define deref
(lambda (ref)
(list-ref the-store ref)))
CS784(PM) 24
A naive model of the store 3/3
(define setref!
(lambda (ref val)
(set! the-store
(letrec
((setref-inner
usage: returns a list like store1, except that position ref1 contains val.
(lambda (store1 ref1)
(cond
((null? store1)
(report-invalid-reference ref the-store))
((zero? ref1)
(cons val (cdr store1)))
(else
(cons
(car store1)
(setref-inner
(cdr store1) (- ref1 1))))))))
(setref-inner the-store ref)))))
CS784(PM) 25
value-of-program
(define value-of-program
(lambda (pgm)
(initialize-store!)
(cases program pgm
(a-program (exp1)
(value-of exp1 (init-env))))))
CS784(PM) 26
value-of clauses explicit-ref ops
(newref-exp (exp1)
(let ((v1 (value-of exp1 env)))
(ref-val (newref v1))))
(deref-exp (exp1)
(let ((v1 (value-of exp1 env)))
(let ((ref1 (expval->ref v1)))
(deref ref1))))
(setref-exp (exp1 exp2)
(let ((ref (expval->ref (value-of exp1 env))))
(let ((val2 (value-of exp2 env)))
(begin
(setref! ref val2)
(num-val 23)))))
CS784(PM) 27
28
IMPLICIT-REFS
• ExpVal = Int + Bool + Proc– references are no longer expressed values.
• DenVal = Ref(ExpVal)• Locations are created with each binding operation:
– at each procedure call, let, or letrec.– This design is called call-by-value, or implicit references.
• Expression ::= set Identifier = Expression– AST: assign-exp (var exp1)– Assignment statement
• Variables are mutable.CS784(PM)
IMPLICIT-REFS examples
let x = 0 in letrec
even(dummy) =
if zero?(x) then 1
else begin
set x = --(x,1);
(odd 888)
end
odd(dummy) =
if zero?(x) then 0
else begin
set x = --(x,1);
(even 888)
end
let g = let count = 0 in proc (dummy)
begin
set count = --(count,--1);
count
end
in let a = (g 11)
in let b = (g 11)
in --(a,b)
CS784(PM) 29
value-of specs
• (value-of (var-exp var) ρ σ) = ( σ(ρ(var)), σ)– environment ρ binds variables to locations
• Given: (value-of exp1 ρ σ0) = (val1, σ1)• Then, (value-of (assign-exp var exp1) ρ σ0)
= ( [27], [ρ(var) = val1] σ1) ;; caution: []• For procedure call, the rule becomes
(apply-procedure (procedure var body ρ) val σ)= (value-of body [var = lc]ρ [lc = val]σ )
CS784(PM) 30
Parameter-Passing Variations
• When a procedure body is executed, – its formal parameter is bound to a denoted value. – It must be passed from the actual argument in the call.
• Natural parameter passing– the denoted value is the same as the expressed value of the
actual parameter (EOPL3 page 75).
• Call-by-value– the denoted value is a reference to a location containing
the expressed value of the actual parameter (EOPL3 section 4.3).
CS784(PM) 32
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.
CS784(PM) 33
CALL-BY-REFERENCE
let p = proc (x) set x = 4
in let a = 3
in begin (p a); a end
let f = proc (x) set x = 44
in let g = proc (y) (f y)
in let z = 55
in begin (g z); z end
• next prog: 11 versus --11
let swap = proc (x) proc (y)
let temp = x
in begin
set x = y;
set y = temp
end
in let a = 33
in let b = 44
in begin
((swap a) b);
--(a,b)
endCS784(PM) 34
call-by-reference
• ExpVal = Int + Bool + Proc• DenVal =Ref(ExpVal)• a new location is created for every
evaluation of an operand other than a variable.
CS784(PM) 35
call-by-ref implementation
(define apply-procedure
(lambda (proc1 val)
(cases proc proc1
(procedure
(var body saved-env)
(value-of body
(extend-env var val saved-env))))))
(call-exp (rator rand)
(let ((proc
(expval->proc (value-of
rator env)))
(arg (value-of-operand rand env)))
(apply-procedure proc arg)))
CS784(PM) 36
37
value-of-operand
(define value-of-operand
(lambda (exp env)
(cases expression exp
(var-exp (var) (apply-env env var))
(else
(newref (value-of exp
env))))))CS784(PM)
variable aliasing
let b = 3 in let p= proc (x) proc(y)
begin
set x = 4;
y
end
in ((p b) b)
• both x and y refer to the same location
• Yields 4• aliasing makes it
difficult to understand programs.
CS784(PM) 38
Lazy Evaluation
• Under lazy evaluation, an operand in a procedure call is not evaluated until it is needed by the procedure body.
• Sometimes in a given call a procedure never evaluates some of its formal parameters.
• This can potentially avoid non-termination.
letrec infinite-loop (x) = infinite-loop(--(x, --1))
in let f = proc (z) 11
in (f (infinite-loop 0))• infinite-loop does not
terminate.• above prog returns 11
under lazy evalCS784(PM) 39
40
Lazy Evaluation Terms
• 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
CS784(PM)
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, and thereafter refers to the saved value.– an example of memoization.
CS784(PM) 41
CALL-BY-NAME
• An operand is frozen when it is passed unevaluated to the procedure
• Operand is thawed when procedure evaluates it
• DenVal = Ref(ExpVal + Thunk)
• ExpVal = Int + Bool + Proc
(define-datatype
thunk thunk?
(a-thunk
(exp1 expression?)
(env environment?)))
CS784(PM) 42
value-of-operand
(define value-of-operand
(lambda (exp env)
(cases expression exp
(var-exp (var) (apply-env env var))
(else
(newref (a-thunk exp env))))))
CS784(PM) 43
call by name design
CS784(PM) 44
(var-exp (var)
(let ((ref1 (apply-env env var)))
(let ((w (deref ref1)))
(if (expval? w)
w
(value-of-thunk w)))))
value-of-thunk: Thunk→ExpVal
(define value-of-thunk
(lambda (th)
(cases thunk th
(a-thunk (exp1 saved-env)
(value-of exp1 saved-env))))
CS784(PM) 45
call by need
• Alternatively, once we find the value of the thunk, we can install that expressed value in the same location, so that the thunk will not be evaluated again.
• This is an instance of a general strategy called memoization.
CS784(PM) 46
memoization
CS784(PM) 47
(var-exp (var)
(let ((ref1 (apply-env env var)))
(let ((w (deref ref1)))
(if (expval? w)
w
(let ((val1 (value-of-thunk w)))
(begin
(setref! ref1 val1)
val1))))))
Lazy Evaluation Summary
• In the absence of (side) effects, it supports reasoning about programs in a particularly simple way.
• The effect of a procedure call can be modeled by replacing the call with the body of the procedure, with every reference to a formal parameter in the body replaced by the corresponding operand.
• This evaluation strategy is the basis for the lambda calculus, where it is called β-reduction.
• Unfortunately, call-by-name and call-by-need make it difficult to determine the order of evaluation, which in turn is essential to understanding a program with effects.
• Thus lazy evaluation is popular in functional programming languages (those with no effects), and rarely found elsewhere.
CS784(PM) 48