Notes about extensions
Extension B, like other extensions, serves three purposes. The extensions will...
- ...give you a much more detailed understanding of the material.
- ...make your interpreter more complete.
- ...provide you a chance to earn some extra credit.
It is extremely important to note that you should not be attempting
any of the Extensions unless you are completely finished with all
previous parts. You will lose far more points for failing to complete
required sections of the interpreter than you could possibly gain by
completing all of the Extensions.
You may attempt to complete the Extensions at any point during the
project. You do not need to attempt them after a specific required
portion of the project. In addition, you should not run
handin37 separately to hand in an extension: simply hand in
the extension along with the next required section you complete.
Finally, since these extensions are considered "advanced" features, you will
receive far less guidance in completing them than you will receive for
the required portions of the project. This does not mean that you
cannot ask questions; rather, I will provide less information to you
up front, forcing you to work out details on your own.
Implementing trace and untrace
Though it may not seem logical to implement trace and
untrace before implementing lambda, you will
actually be able to complete the entire implementation of these two
functions without much of an idea of how lambda will
eventually operate, and you can test your current implementation of
trace on the primitive procedures. Of course, once you add
lambda to your interpreter, you will want to go back and make
sure that everything works. Chances are that, with one minor detail,
your implementation should continue to work.
Play around with trace in DrRacket to make sure you
understand how it works. Note that in DrRacket, you can trace
functions that you write yourself, but you can not trace primitive
functions, so you will need to write your own functions in order to
experiment with trace. In our implementation, we will
support tracing both our own functions and primitive functions.
You should also read the Racket documentation for trace.
Select the entry that says:
trace provided from racket/trace
A few pieces of advice:
- Your test for whether or not you are trying to apply a traced
procedure should be done in eval‑application.
- Be sure you can handle expressions such as (trace a b c)
(which traces all three functions); on its own, (trace) does
nothing.
- Be sure you correctly handle what happens if an already-traced
function is traced. For example (the colors are just for easy reading, you should not try to output specially colored text):
> (define f (lambda (x) (+ x 1)))
> (trace f)
(f)
> (f 5)
|(f 5)
|6
6
> (trace f)
(f)
> (untrace f)
(f)
> (f 5)
6
- The "detail" that you may need to go back and fix when you get to
lambda in Part 5 -- unless you anticipated well or read the
documentation page -- is how to deal with nesting the output of traced
procedures which call other traced procedures (or themselves
recursively). You can test that out once you write lambda in
Part 5.
- In addition to allowing tracing of primitive functions, your
version of trace can be even more powerful than the built-in
trace -- if you want it to be. In fact, you may find that it is
actually easier to allow for this "more powerful" version than to try
and mimic DrRacket's version. Once you are able to define your own procedures
using lambda, you'll be able to trace
procedures that are defined locally inside other procedures; the
DrRacket version of trace will not let you do that.
Extension B may be completed at any stage of the final project and does not need to be turned in separately.