This is an optional extra credit assignment, provided to allow those of you who struggled in the pre-midterm part of the course a chance to demonstrate mastery of that material. Since students have earned low grades on different assessments (different assignments, midterm, etc), I don't have a fixed policy for handling the extra credit points (such as "this replaces the grade on hwk n"). However, if your class grade is adversely affected by a low score on material early in the course, I will use your performance on this assignment to offset it in a reasonable manner.
The midterm introduced a new kind of binding called
i-letrec
which was a cross between let
and
letrec
. As a reminder,
(i-letrec ([var_1 expr_1] ... [var_n expr_n]) body)
extends its inherited environment as follows:
letrec
binds all the var_i in each expr_k, and
let
binds none of them).
letrec
and let
, too)
Semantics: i-letrec
evaluates all the expr_i in
parallel, assigns each value to the corresponding var_i, then
evaluates and returns the value of body. Any use of a var_i in an
expr_j must be in the body of a function. (This is also expected of
letrec
terms.) Thus, (i-letrec ([x 3] [y x])
y)
is illegal, but (i-letrec ([x 3] [y (lambda () x)])
(y))
is not.
Add i-letrec
to the AFunRecExp interpreter (i.e.,
the interpreter with functions, let, recursion, function application,
variables, and arithmetic; don't worry about having conditionals and
assignment).
Unlike let
and letrec
,
i-letrec
isn't very useful unless you can bind more than
one variable in the same i-letrec
expression. Your
solution must support at least 3 bound variables (let
and
rec
should continue to support just one). For a bit of
extra credit, implement iletrecE
so it can handle an
arbitrary (though positive) number of bound variables (instead of the
fixed number of 3 variables).
Would your i-letrec
implementation need to change
if we added setE
to the language? Either explain all of
the places where your code would break or justify why it would still
work. Provide a concrete test case that you would use to test your
interpreter if it included setE
(you may use Scheme
syntax for the example) -- obviously you do not need to actually
run this test case since your interpreter need not implement
setE
.
Guidelines
i-letrec
should
be the same as for a variable bound with other binding constructs.
We want to augment our interpreter with a basic feature for code
profiling. Specifically, we want to add a construct
call-count
that takes no arguments and returns the number
of function calls (applications) that have been made since the program
began executing. The count starts at 0 whenever interp
is called from the prompt; each function application increases the
count by 1. Here are some examples:
{+ {call-count} 4} = 4 (since no calls have been made, call-count returns 0) {with {f {fun x {+ x 1}}} {with {n {if0 {f 5} {f 6} {f {f 3}}}} {call-count}}} = 3
Add call-count
to an interpreter for AFunExp with
static scoping and eager evaluation. Your solution must be fully
functional; in other words, you may not use assignment operators
(set!, boxes, structure mutation) anywhere in your solution.
Should call-count
be treated statically or
dynamically in the presence of closures? Justify your answer, and
explain how your code implements it.