@comment(Hey, EMACS, this is -*- SCRIBE -*- input)
@make(6001)
@set(chapter=1)
@set(page=1)

@PageHeading(even,
             left "@Value(Page)",
             right "6.001 -- Fall Semester 1985")

@PageHeading(odd,
             Left "Problem Set 9",
             right "@value(page)")

@begin(center)
MASSACHUSETTS INSTITUTE OF TECHNOLOGY
Department of Electrical Engineering and Computer Science
6.001 Structure and Interpretation of Computer Programs

Problem Set 9
@end(center)
@blankspace(0.25 in)

Issued: 12 November, 1985

Due: Wednesday, 27 November, 1985, for ALL sections.

Reading Assignment: finish chapter 4


@b[Exam notice:] Exam #2 will be held on Wednesday, November 20, 1985.
The exam will be held in Walker Memorial Gymnasium (50-340) from 5-7PM
xor 7-9PM.  You may take the exam during either one of these two
periods, but students taking the exam during the first period will not
be allowed to leave the room until the end of the period.  The exam is
closed book, but you may bring with you two 8-1/2 by 11 inch sheets of
paper with notes that you have prepared in advance.  The quiz will
cover material from the beginning of the semester through problem set
8, and in the text through the end of chapter 3.
 
This problem set contains two distinct parts.  The first, which is
rather difficult, asks you to modify the Lisp interpreter to allow for
normal-order evaluation.  The second part should be easy -- it
consists of trying some simple interactions with the query system.

@section(Normal-order evaluation)

This section asks you to modify the Lisp evaluator to allow for
procedures that pass parameters in a ``call-by-name'' style.  The
necessary modification is outlined in section 4.2.1 of the
text.  Although this modification does not require writing a great
deal of code, you will need to have a good understanding of the
evaluator in order to do this problem set.  Read the beginning of
chapter 4 and plan your work carefully before coming to the lab.  

@paragraph(Pre-lab assignment)

The following problem should be done before beginning work on the lab
assignment: 

The interpreter given in Section 4.1, unlike the full Scheme
interpreter, does not have an @a[if] construct, only a @a[cond].
Louis Reasoner claims that in fact @a[if] is unnecessary, since we
could define it as an ordinary Lisp procedure:

@begin(example)
(define (if predicate action alternative)
  (cond (predicate action)
        (else alternative)))
@end(example)

Explain why this does not work.  In particular, suppose you use this @a[if]
procedure to define @a[factorial]:

@begin(example)
(define (factorial n)
  (if (= n 1)
      1
      (* n (factorial (- n 1)))))
@end(example)

What happens when you attempt to evaluate @a[(factorial 3)] ?  Why. 

We realize that you have seen this problem before, but we want you to
be thinking about this issue before going on to the rest of the
assignment.  Note also that you cannot try out the above definitions
in an ordinary Scheme, since Scheme's own definition of @a[if] will
interfere with the one you attempt to define.  (Ordinarily, there is
nothing to stop you from re-defining Scheme primitives, but @a[if] is
implemented in Scheme as a special form.)

@paragraph(In the laboratory)

The file
@a[ps9-ev.scm] contains a copy of a simple Scheme interpreter
written in Scheme, essentially the same as the one in Section 4.1.
There are a few minor differences, of which the most important are:
@begin(itemize)
The procedures @a[eval] and @a[apply] have been renamed @a[mini-eval] and
@a[mini-apply], so as not to interfere with Scheme's own @a[eval] and
@a[apply] operators.

The interface to the underlying Scheme system via
@a[apply-primitive-procedure] is handled somewhat differently from in
the book.
@end(itemize)
See the attached code for details.

If you load this file into Scheme and type @a[(initialize-evaluator)],
you will find yourself typing at the driver loop.  Please note that
this Scheme running in Scheme contains no error system of its own.  If you hit an
error or type @c[ctrl-G], you will bounce back into Scheme.  Also, you
must type in procedure definitions directly to the driver loop, since
there is no interface to NMODE.@foot{However, you can use the editor
to write an ordinary Scheme procedure that calls @a[mini-eval] in
order to predefine various things in the global environment.  Run
@a[initialize-evaluator], then type @c[ctrl-G] to get back to
Scheme, run your procedure to predefine things, then reenter the
interpreter by typing @a[(driver-loop)].}

In order to help you keep from becoming confused, the driver loop uses
the prompt @a[MC-EVAL==>] instead of the ordinary Scheme prompt.

Start up the interpreter and try a few simple expressions.  If you
bounce out into Scheme, you can re-enter the interpreter by typing
@a[(driver-loop)].  If you get hopelessly fouled up, you can run
@a[initialize-evaluator], but this initializes the global
environment, and you will lose any definitions you have made.

Also, it is instructive to run the interpreter while tracing
@a[mini-eval] and/or @a[mini-apply], to see just how the evaluator
works.  (You will also probably need to do this while debugging your
code for this assignment.)  If you do trace these procedures, you may
want to use the Scheme procedures @a[print-breadth] and
@a[print-depth] to avoid printing huge listings of the @a[env]
argument to these procedures (printed by the tracer), which is in
general a circular structure.  See the Chipmunk manual for information
on the use of @a[print-breadth] and @a[print-depth].

For this assignment, you will need to modify a few of the procedures
in the interpreter, and also write a few new procedures.  To help you,
@a[ps9-mods.scm] contains some procedures that you may need to modify.
The entire @a[ps9-ev.scm] file can be LOADed directly into Scheme
without your worrying about editing it.  You can load @a[ps9-mods.scm]
into an NMODE buffer, which you can edit while doing this assignment.
You should realize, however, that depending on how you do the problem
set, there may be other procedures from the evaluator that you may
want to modify.  You may want to ask a TA about how to move blocks of
code between editor buffers.


@paragraph(Lab problem 1)

Section 4.2.1 of the text sketches the modification you are to make
to the evaluator.  Filling in the details will require modifying a few
of the procedures already given, and also writing new procedures (for
example, to handle thunks).  Be sure to test your implementation.  For
all of these lab problems, turn in listings of the procedures that you
write and modify.

As part of your testing process, try the following example from p. 316
of the text:

@begin(example)
MC-EVAL==> (define (try (delayed a) (delayed b))
             (cond ((= a 0) 1)
                   (else b)))

MC-EVAL==> (try 0 (/ 1 0))
1
@end(example)

Two more hints in designing the implementation:
@begin(itemize)
Be sure to tell @a[user-print] about thunks, so if it is
passed a thunk to print, it will first undelay it.

Try the example in Exercise 4.10 of the text, which
illustrates a easily-made bug in the undelaying strategy.

@end(itemize)


@paragraph(Lab problem 2)

When you think your implementation is working, try defining @a[if] as
a procedure, as shown on page 317 of the text.  Use @a[if] in
defining @a[factorial], as in the pre-lab exercise.  This definition
should work.  Also, try Exercise 4.9 of the text. 

@paragraph(Lab problem 3)

Do exercise 4.11 of the text.

Test your implementation by trying the following examples:

@begin(example)
MC-EVAL==> (define (integers-from n)
             (cons n (integers-from (+ n 1)))

MC-EVAL==> (define integers (integers-from 1))

MC-EVAL==> (car integers)
1

MC-EVAL==> (car (cdr integers))
2
@end(example)

@paragraph(Lab problem 4)

Last week, in problem set 8, you helped Louis Reasoner understand Ben
Bitdiddle's @a[unique-pairs] procedure.  Louis, you recall, had
suggested that Ben could define @a[unique-pairs] like this:

@begin(programexample)
(define (unique-pairs s)
  (interleave (map (lambda (y) (list (head s) y))
                   (tail s))
                   (unique-pairs (tail s))))

(define (interleave s t)
  (cons-stream (head s)
               (interleave t (tail s))))
@end(programexample)
Unfortunately, this didn't work.  Ben had to define his procedure
in terms of a special @a[interleave-delayed] that included explicit
@a[delay]s and @a[force]s.

Demonstrate that Louis' idea is essentially correct, if you use your
new interpreter and the ``stream-like list'' implementation you
constructed above.  That is, Ben should be able to write:
@begin(programexample)
(define (unique-pairs s)
  (interleave (map (lambda (y) (cons (car s) (cons y '())))
                   (cdr s))
                   (unique-pairs (cdr s))))

(define (interleave s t)
  (cons (car s)
        (interleave t (cdr s))))

@end(programexample)
except that @a[interleave] must have its arguments
declared suitably @a[delayed].   Give the appropriate new definition of
@a[interleave].   Also define @a[map] and test the code by finding the
list (stream) of unique pairs of integers:
@begin(programexample)
MC-EVAL==> (define pairs (unique-pairs integers))

MC-EVAL==> (car integers)
??

MC-EVAL==> (car (cdr integers))
??
@end(programexample)
What are the first 5 items in the list @a[pairs]?

(Note that in new definition of @a[unique-pairs], we wrote
@begin(programexample)
(cons (car s) (cons y '()))
@end(programexample)
instead of @a[(list (car s) y)] to get round
the fact that the metacircular evaluator code does not have @a[list]
defined as a primitive.)

@section(Using the query system)

For this section of the problem set, we would like you to load the
query system and try out a few simple queries and rules so that you
get a feeling for how it works.

If you have just finished with the metacircular evaluator above, it's
probably a good idea to clear out your workspace (save any files you
want to keep, logout, and then login again).  Then use the
@i[load-problem-set] command and specify the problem set ``number'' as
@a[query].  This will load into Scheme a copy of the query language
described in Chapter 4 of the text.  It will also load into Scheme and
into an NMODE buffer a file called @a[query-mods.scm] that contains the
data base for the Itsey-Bitsey Machine Corporation (see page 339)
together with a few rules.  (The source code for the query evaluator
code itself will not be loaded into NMODE -- only into Scheme.)

@include(<6001.ps>query-driver.txt)

@include(<6001.ps>query-editzap.txt)

@comment[Here we set up the name of the variable (in the mods file)
 containing the data base.  This parameterizes the included file.]
@string(database-variable="ibm-data-base")
@include(<6001.ps>query-database.txt)


@paragraph(Query system lab problems)

Do exercises 4.27, 4.28, and 4.29.  The first two exercises require
you only to enter queries.  For the third exercise, you will need to
define a rule.

@newpage()
Here is the initial database that appears in the @a[query-mods.scm] file:

@begin(programexample)
@include(ps9-qdata.scm)
@end(programexample)

@newpage()
@begin(programexample)
@include(ps9-MCEVAL.SCM)
@end(programexample)
