





		MASSACHVSETTS INSTITVTE OF TECHNOLOGY
      Department of Electrical Engineering and Computer Science


      6.001 -- Structure and Interpretation of Computer Programs
		      Quiz 2   Fall Term   1993


			       SOLUTIONS










Overall:

   This quiz turned out very nicely.  There were many very high
   grades, indicating that most of the students understood the 
   material very well.  On the whole, we are very pleased with 
   your performance.





The problems were graded as follows:


		    Problem  Grade
		    ---------------
		    | 0    |   2  |
		    ---------------
		    | 1    |  24  |
		    ---------------
		    | 2    |  24  |
		    ---------------
		    | 3    |  25  |
		    ---------------
		    | 4    |  25  |
		    ---------------
		    |TOTAL | 100  |
		    ---------------

Problem 1. The following problem is a simplified version of one that was
assigned for tutorial as part of problem set 6.

Suppose that we evaluate the following sequence of definitions:

(define (make-flip)
  (let ((state 1))
    (lambda ()
      (if (= state 0)
          (set! state 1)
          (set! state 0))
      state)))

(define flip (make-flip))

(define flap1 (flip))

(define (flap2) (make-flip))

(define flap3 (flap2))

(define (flap4) (flip))

What is the value of each of the following expressions (evaluated in the
order shown).  If you think the expression evaluates to a procedure, then
write "procedure."  If you think that the evaluation will produce an error,
then indicate what the error would be.

flap1 ===>    0

flap2 ===>    procedure

flap1 ===>    0

(flap1) ===>  error -- 0 is not an applicable procedure

(flap2) ===>  procedure

(flap3) ===>  0

(flap4) ===>  1

(flap3) ===>  1


     These were graded at 3 points each, with some freedom to
     give partial credit for an incorrect description of the
     error condition.






	     ENVIRONMENT DIAGRAM FOR PROBLEM 2





Problem 2. The following sequence of commands will make a bank, an
account, and a depositor.  (The MAKE-BANK and MAKE-DEPOSITOR procedures
here refer to the procedures on pages 3 and 4 of the handout -- NOT
the ones that use the object system from PS6.)

(define bcci (make-bank 'bcci 100000000.00 .01 .005))

(define bcci1 ((bcci 'open-account) 'bcci1))

(define person1
  (make-depositor 'Michael-Milken 100000 200000 50000 1000
                  (list bcci1)))

Part A. The incomplete environment diagram shown below describes the
situation after these three definitions have been evaluated.  There are
eight pointers that are incomplete.  The stubs are labeled with a letter
and a question mark (A? through H?).  Such a pointer should point to a
labeled item in the diagram.  Procedures are labeled Pn, environment frames
are labeled En, and lists are labeled Ln (as in "P2" or "E3" or "L1").
Complete the diagram by extending the pointer stubs to the correct places.
Summarize your answers by completing the table at the bottom of the page.
ONLY THE TABLE WILL BE GRADED.  An extra copy of the environment diagram,
for scratch work, is given on the facing page.




     The answers requiring the completion of the environment
     diagram were worth 2 points for each one -- a total of 16
     points.  The correct environment diagram is shown on the
     facing page.




Your Answers:   A:_P3_    B:_P3_    C:_P9_    D:_E3_  


                E:_P6_    F:_P9_    G:_P6_    H:_P9_  

Problem 2 (continued).

Part B. Suppose we now execute 

              ((bcci1 'transact) 1000000 'donald-trump)

This will create some new environment frames.  One of these frames
will contain a binding for the variable MESSAGE.  To which of the
following frames will this frame point as its enclosing environment?

     The correct answer is E6, because BCCI1 is the
     message-accepting procedure P9, and its environment is E6.
     This was 4 points.






	
Part C. Suppose we make another account in the BCCI bank.  One of the
environment frames that used to have one frame pointing at it will now
have two frames pointing at it.  Which frame is this?  Circle the
correct answer.

     The correct answer here is E3, because constructing another
     account will call procedure P3, making frames analogous to
     E5 and E6.  The frame analogous to E5 will point at the same
     frame as E5 did -- E3.
     This was 4 points.



		      CODE LISTING FOR PROBLEM 3


1     (define *the-banks* '())

2     (define (make-bank bank-name assets return-rate account-rate)
3       (let ((accounts '()))
4         (define (open-account account-name)
5           (let ((balance 0))
6             (define (get-balance)
7               balance)
8             (define (transact amount depositor-name)
9               (write-line
10                `(transaction ,bank-name ,account-name ,amount ,depositor-name))
11              (cond ((not (memq the-account accounts))
12                     'inactive-account)
13                    ((> (+ balance amount) 0)
14                     (set! balance (+ balance amount))
15                     (set! assets (+ assets amount))
16                     balance)
17                    (else 'insufficient-funds)))
18            (define (the-account message)
19              (cond ((eq? message 'transact) transact)
20                    ((eq? message 'get-balance) get-balance)
21                    (else (error "Unknown action -- ACCOUNT"))))
22            (set! accounts (cons the-account accounts))
23            the-account))
24        (define (compute-interest)
25          (set! assets (+ assets (* return-rate assets)))
26          (for-each (lambda (account)
27                      (let ((interest
28                              (* account-rate ((account 'get-balance)))))
29                        (set! assets (- assets interest))
30                        ((account 'transact) interest 'monthly-interest)))
31                    accounts)
32          assets)
33        (define (the-bank message)
34          (cond ((eq? message 'open-account) open-account)
35                ((eq? message 'compute-interest) compute-interest)
36                (else (error "Unknown message -- BANK"))))
37        (set! *the-banks* (cons the-bank *the-banks*))
38        the-bank))

Problem 3. Suppose that we want to use our simple message-passing bank
(p.3 of the handout) in a parallel execution system, as in Problem Set 7.

On the facing page you will find the definition of MAKE-BANK from the
handout.  The lines are numbered for reference.

The code is full of bugs that may arise in concurrent execution.  One kind
of concurrent execution is if the depositors are allowed to perform
transactions concurrently.  More complex problems arise if new accounts may
be made or if new banks may be made concurrently with depositor activity.

     In parts A and B below the only variables that matter are
     ones that are assigned values, and that may be shared
     between processes.  Variables which are bound but not
     assigned are irrelevant because their values never change in
     a context.  Variables that are assigned but not shared
     (there are none here) are also safe.


Part A. Which of the following variables that occur in this code need to be
protected by serializers if the only concurrency is that the depositors are
allowed to perform transactions concurrently?  Circle the vulnerable
variables.

     The only variables that are vulnerable here are ones that
     are shared among depositors.  If two depositors share an
     account then the BALANCE in that account is vulnerable.
     Also, if two depositors share a bank (if they share an
     account they certainly share the bank) then the ASSETS of
     the bank are vulnerable.  Thus the answer is:

		      assets  balance

     We gave 2 points for each correctly identified variable and
     took off 2 points for each incorrectly identified variable
     here.  One could not get less than zero points.


Part B. Which of the following variables that occur in this code need to be
protected by serializers if the depositors are allowed to execute
concurrently, and if new banks, new depositors and new accounts may be made
at any time?  Circle the vulnerable variables.

     This is just like the last question, except that the range
     of concurrent execution is expanded.  The only additional
     variables that become vulnerable are *THE-BANKS* and
     ACCOUNTS.  Thus the correct answer is:

	    *the-banks* accounts assets  balance

     We gave 1 point for each correctly identified variable and
     took off 1 point for each incorrectly identified variable
     here, partially compensating for the overlap with part A.
     One could not get less than zero points.

Problem 3 (continued).

Part C. Suppose we want to protect the assignment to ACCOUNTS on line 22
using a serializer named ACCOUNTS-SERIALIZER, constructed by
(MAKE-SERIALIZER).  At which point in the code should the
ACCOUNTS-SERIALIZER be constructed?

       It should be included with the LET bindings in line 3.

         We gave 3 points for this answer.


In the space provided below, give a brief justification of your answer.

     We need a separate ACCOUNTS-SERIALIZER for each bank.  If we
     made it global to the bank we would prevent two banks from
     concurrently making their own private accounts.  But, the
     accounts-serializer must persist as part of the bank and be
     available to OPEN-ACCOUNT.  It may not be made afresh with
     each call to OPEN-ACCOUNT or with each transaction, as that
     would not adequately protect the ACCOUNTS variable.

         We gave 3 points for any coherent explanation.
         An adequate explanation explained both why the
         serializer could not be more global or more 
         local than the frame of the bank.


In the space provided below, write the replacement for line 22 needed to
protect the ACCOUNTS variable.

  ((accounts-serializer
    (lambda ()
      (set! accounts (cons the-account accounts)))))

         We gave 3 points for this answer, and we took of 1
         point for leaving out the (lambda ()...) or for
         missing the extra parens.


Part D. Suppose we do not protect any variables other than ACCOUNTS, as in
part C above.  In the space provided below, give an example illustrating
how concurrent depositor activity can lead to erroneous results.

     If 2 depositors both have access to the same account, and
     both attempt to perform a transaction at the same time, then
     the transactions may produce an inconsistent balance.  For
     example, suppose the balance starts out at $150.  Suppose A
     deposits $5 and B deposits $10 at the "same time".  Both may
     fetch the balance and perform the addition, getting $155 and
     $160, respectively.  Then both may assign the BALANCE
     variable, but only one will be the last to do the
     assignment. Suppose it is B.  Then the balance will be $160,
     losing A's deposit.  Other scenarios were accepted.  For
     example, it is possible for 2 withdrawals to add up to an
     overdraw, yet both get approved and each gets done,
     producing a negative balance.  

  We gave 4 points for understanding the shared assigned variable
  and we gave 4 points here for a coherent example.

Problem 4

PART A. Louis Reasoner suggests that we could have simplified the
object-oriented code for MAKE-SLEAZY-BANK on page 9 of the handout by using
the variable ASSETS directly instead of calling (ASK SELF 'GET-ASSETS) and
(ASK SELF SET-ASSETS!).  Thus, he suggests that the MAKE-SLEAZY-BANK
procedure should be rewritten as follows.  (The changed lines are marked
with stars.)

(define (make-sleazy-bank bank-name assets return-rate account-rate)
  (let ((honest-bank (make-bank bank-name assets return-rate account-rate)))
    (define (compute-interest-dishonestly self)
      (let ((old-assets assets))                                        ;***
        (set! assets (+ old-assets (* return-rate old-assets)))         ;***
        (for-each
	 (lambda (account)
	   (let ((interest (* account-rate (ask account 'get-balance))))
	     (let ((rounded-interest (floor interest)))
	       (set! assets (-  assets interest))                       ;***
	       (ask account 'transact rounded-interest 'monthly-interest)
	       (ask *the-stash*
		    'transact
		    (- interest rounded-interest)
		    'anonymous-donor))))
	 (ask self 'get-accounts))
        assets))                                                        ;***
    (define (me message)
      (if (eq? message 'compute-interest)
          compute-interest-dishonestly
          (get-method honest-bank message)))
    me))

In the space below, give a clear, concise explanation of why Louis's
suggestion is unacceptable.  Sketch an example showing how Louis's program
will result in incorrect behavior.  (In this question, you should assume
that the system is running as a simple synchronous system--not the parallel
execution scenario of problem 3.)

     The problem is that the ASSETS variable in Louis's sleezy
     bank is not the same as the ASSETS in the internal honest
     bank.  Thus, when interest is to be computed, the changes in
     ASSETS will be inconsistent.  We deposit in the honest
     bank's account the rounded interest but we only subtract
     that from the sleezy bank's ASSETS.  So the honest bank's
     total will increase rather than remain constant (not
     counting the deposit due to the return on loans).  This can
     be caught by even Poopers and Liebrand.

     The fact that there are two inconsistent ASSETS variables can
     be illustrated with the following simple example:

       (define lsb (make-sleazy-bank 'lsb 1000 .1 0.0))

       (ask lsb 'get-assets)
       ;Value: 1000

       (ask lsb 'compute-interest)
       ;Value: 1100.

       (ask lsb 'get-assets)
       ;Value: 1000

     We gave 6 points for the explanation and 6 points for
     any coherent attempt at an example.

PART B. Now that Louis understands why his previous idea doesn't work, he
has another suggestion for changing MAKE-SLEAZY-BANK.  He wants to replace
(ASK SELF 'GET-ASSETS) in the original code by (ASK HONEST-BANK
'GET-ASSETS).  That is, he wants to rewrite the procedure as follows (the
starred lines indicate the changes from the version given in the handout):

(define (make-sleazy-bank bank-name assets return-rate account-rate)
  (let ((honest-bank (make-bank bank-name assets return-rate account-rate)))
    (define (compute-interest-dishonestly self)
      (let ((old-assets (ask honest-bank 'get-assets)))	          ;***
	(ask honest-bank 'set-assets!
	     (+ old-assets (* return-rate old-assets)))	          ;***
	(for-each
	 (lambda (account)
	   (let ((interest (* account-rate (ask account 'get-balance)))
		 (assets (ask honest-bank 'get-assets)))          ;***
	     (let ((rounded-interest (floor interest)))
	       (ask honest-bank 'set-assets! (-	assets interest)) ;***
	       (ask account 'transact rounded-interest 'monthly-interest)
	       (ask *the-stash* 'transact
		    (- interest rounded-interest)
		    'anonymous-donor))))
	 (ask self 'get-accounts))
	(ask honest-bank 'get-assets)))				  ;***
    (define (me message)
      (if (eq? message 'compute-interest)
	  compute-interest-dishonestly
	  (get-method honest-bank message)))
    me))

Check the answer that best describes the effect of Louis's modification:

_X_ Louis's sleazy bank has the same behavior as the original.  But if we
    define a new kind of bank that inherits behavior from a sleazy bank, then
    the new kind of bank could have different behavior when inheriting from
    Louis's sleazy bank than when inheriting from the original sleazy bank.

Give a brief explanation of your answer in the space provided below.

     As it stands, Louis's bank treats its ASSETS by asking the
     honest bank.  Since that is what GET-ASSETS does anyway, the
     two banks will behave the same.  On the other hand, suppose
     we had a new kind of bank that inherited from a sleezy bank
     and had its own special GET-ASSETS method.  In Louis's case
     COMPUTE-INTEREST would use the honest bank's GET-ASSETS
     method.  In the original code, which always refers to SELF
     the special GET-ASSETS method would be used.


     For checking off the first or second choice you got no
     credit, independent of your explanation.

     For checking off the third choice you could get up to 7
     points if you had a good explanation.

     For checking off the fourth choice you would get at least 3
     points independent of the explantion.  You could receive 10
     more points for a clear explantion that talked specificaly
     about what happend if a new type of bank inherited from
     Louis' sleazy bank.  Restating the paragraph was not sufficent.
