2. Statement of the problem
● A new language AF (Annot-Formal) which extends Mutable
Pairs (chapter 4)
● AF supports:
– Call-by-value
– Call-by-reference
– Call-by-need
● The type of calling is established at the point of procedure
declaration
● New test cases are added to support the differences between the
types of calling
3. Convention
● The type of calling is distinguished using the following
annotations in front of the procedure argument:
– value
– ref
– lenes - for lazy evaluation
● There is no implicit annotation
● All proc declarations are required to specify the convention
let p = proc (value x) x
in (p 10)
4. Changes in the lexical spec &
grammar
● Lexical spec supports 3 annotations:
(annot ("lenes") symbol)
(annot ("value") symbol)
(annot ("ref") symbol)
● The grammar for proc is changed to require the type of
parameter passing in the procedure declaration
(expression
("proc" "(" annot identifier ")" expression)
proc-exp)
5. Abstract Data Types
● ExpVal = Num + Bool + Proc + Reference + Mutpair + Thunk
● Thunk = (Expression, Environment)
● Thunk is used to store the information for evaluating an
expression within an environment. Its evaluation is delayed until
the last moment.
6. Semantics of the changed expressions
● I keep in the store a global variable mode, which will
save the annotation
● I update its value once the procedure is declared
● I use mode in call-exp, var-exp and apply-procedure
interp.scm
(define mode "")
(define value-of-program
(lambda (pgm)
(initialize-store!)
(set! mode (newref 'unspecified))
(…... ))))))
interp.scm
(proc-exp (annot var body)
(begin
(setref! mode annot)
(proc-val (procedure var body env))
))
7. Semantics of the changed expressions
interp.scm
(call-exp (rator rand)
(let ((proc (expval->proc (value-of rator env)))
(arg (if (equal? (deref mode) 'value)
(value-of rand env)
(value-of-operand rand env))))
(apply-procedure proc arg)))
In call-exp, the argument of
the function is evaluated with
value-of only if we have call
by value.
In case of call by reference
and call by need, the expression is evaluated with value-of-
operand:
If the operand is a variable then
get its value from the environment.
If it's an expression not bound to a
variable, evaluate it only if we don't
have call by need, and also create
a new reference for it.
(define value-of-operand
(lambda (exp env)
(cases expression exp
(var-exp (var) (apply-env env var))
(else
(newref (if (equal? (deref mode) 'lenes)
(a-thunk exp env)
(value-of exp env)))))))
8. Semantics of the changed expressions
● A change in apply-procedure
● If call by value – create a new reference, otherwise
use the same reference (which is either bound before
in value-of-operand or bound at the creation of the
variable)
(define apply-procedure
(lambda (proc1 arg)
(cases proc proc1
(procedure (var body saved-env)
(let ((r (if (equal? (deref mode) 'value) (newref arg) arg)))
(let ((new-env (extend-env var r saved-env)))
(value-of body new-env)))))))
9. Semantics of the changed expressions
Call by need implementation
● A lazy reference evaluation
(define value-of-operand
(lambda (exp env)
(cases expression exp
(var-exp (var) (apply-env env var))
(else
(newref (if (equal? (deref mode) 'lenes)
(a-thunk exp env)
(value-of exp env)))))))
(define-datatype thunk thunk?
(a-thunk
(exp1 expression?)
(env environment?)))
A thunk has an (unevaluated)
expression and an environment
attached.
10. Semantics of the changed expressions
Call by need implementation
● Modification in var-exp:
interp.scm
(var-exp (var)
(if (not (equal? (deref mode) 'lenes))
(deref (apply-env env var))
; else, lazy evaluation:
(let ((ref1 (apply-env env var)))
(let ((w (deref ref1)))
(if (expval? w) w
(let ((v1 (value-of-thunk w)))
(begin
(setref! ref1 v1)
v1)))))))
If non-lazy evaluation or lazy evaluation of
an expval → get the value of the variable
from the environment
If lazy evaluation of a thunk → evaluate it
using value-of-thunk
data-structures.scm
(define value-of-thunk
(lambda (th)
(cases thunk th
(a-thunk (exp1 saved-env)
(value-of exp1 saved-env)))))
The procedure's argument stays as a thunk until it's
needed in the body and will be evaluated as a var-exp.
11. Examples of test cases
test1.scm
"newpair (let p = proc(value x)
set x = 4
in let a = 3
in begin
(p a);
a
end, 3)"
test2.scm
"newpair (let p = proc(ref x)
set x = 4
in let a = 3
in begin
(p a);
a
end, 4)"
test4.scm
"newpair (let swap = proc (ref x)
proc (value 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)
end , 0)"
Each test file has a tuple:
“newpair (AF-lang-code, expected value)”
12. Examples of test cases
test6.scm
"newpair (let p = proc(value f) proc(value x)
-(((f f) -(x,1)), -(0,x))
in let newp = proc(lenes n) 25
in (newp ((p p) 5))
, 25)"
Using value or ref instead of lenes → the
program doesn't terminate
> (run-all)
Runs all previous test cases from
tests.scm, where I have modified
the syntax of proc
> (run-all-files)
Runs all additional tests defined in
new files, folder tests
> (run-everything)
Runs both of the above
In top.scm – 2 new functions:
run-file: reading and interpreting one test file
run-all-files: comparing the expected output with
the actual output, for all test files