CS 334
Programming Languages
Spring 2002

Solutions to Assignment 6
Due Friday, 4/11/2002


  1. Do problem 8.18 on page 8-34. Note that each successive function is nested inside the previous. I.e., env contains a, which contains b, which contains c. Also env calls a, which calls b, which calls c, which then calls b again.

    a. After the first call to procedure a, there is an activation record for env and on top of that, one for a. The access (static) and control (dynamic) links for a both point to env.

    a'. After the second call to b, we have the following:

    	
    	         ______
    			 |  b  |--
    			 _______ |
    			 |  c  | |
    			 _______ |
    			 |  b  | |
    			 _______ |
    			 |  a  |<-
    			 _______
    			 | env |
    			 _______
    			 
    All static links go down to the previous activation record except for the one shown from b to a. All dynamic links go down one.

    b. procedure b is found in procedure c by following up two static links from c's activation record. This gives us the activation record for a. In there we find a slot for b that holds the < ip,ep > pair where ip is the instruction pointer for b and ep is a pointer to the activation record for a.

  2. Do problem 8.24 on page 8-36.

    Blocks in C (or Algol or Ada or ...) are guaranteed to always have the same static and dynamic links (because they can only be called from the surrounding code - no remote calls are possible). Thus, you could drop the static link as redundant. You typically do allocate a new activation record for each block, as if there are several disjoint blocks, the computer can reuse stack space by pushing and popping the activation records on top of the same old activation record. You can also drop the return address because the block always returns to the same location. I.e., either the code for the block can be embedded in the surrounding code or the last line of the code for the block can be a jump to the hard-wired location of the next instruction.

    Of course, you may allocate all of the space at once by essentially flattening the code containing the block (essentially erase the block boundaries). However, then you lose the advantages of reusing the space. Most of the time you don't care, but if the blocks introduce large arrays then pushing and popping the activation records can save a lot of space. If you aren't going to allocate the space dynamically then you might as well not even allow local blocks.

  3. Do problem 9.20 on page 9-39.

       frontq(enqueue(enqueue(create,x),y)) 
             = if emptyq(enqueue(create,x)) then y else frontq(enqueue(create,x))
             = frontq(enqueue(create,x))
             = if emptyq(create) then x else frontq(create)
             = x  because emptyq(create) is true
    	

       frontq(enqueue(deque(enqueue(create,x)),y)) 
             = if emptyq(deque(enqueue(create,x))) then y 
                                 else frontq(deque(enqueue(create,x)))
                [but deque(enqueue(create,x)) = if emptyq(create) then create else ..., so]
             = if emptyq(create) then y 
                                 else frontq(deque(enqueue(create,x)))
             = y
    

       frontq(dequeue(enque(enqueue(create,x)),y)) 
             = frontq(enqueue(deque(enqueue(create,x)),y))
    		 
    because empty(enqueue(deque(enqueue(create,x)),y) = false, but we know by above that this last expression is equal to y.

  4. As hinted in class, the THUNK's used to support recursion can also provide direct support for call-by-name parameter passing. Please modify the environment-based interpreter to use THUNK's to support call-by-name rather than call-by-value parameter passing. If you don't like your own, you can use mine, which can be found in the solutions to Assignment 4.

    When you evaluate a function call (with a user-defined function), you should not evaluate the parameter, instead package the (unevaluated) actual parameter and the current environment into a Thunk and insert it into the environment in the same way that you used to insert the value of the actual parameter into the environment. Make sure that the THUNK is handled properly when its value is needed.

    Click here for solution.

  5. Recall the signature EQ and functor PairEQ from Lecture 15. Please write a functor ListEQ that takes a structure P:EQ and constructs another structure matching signature EQ such that the elements of type t are lists of elements from type P.t, and two elements are equal if they are the same length and corresponding elements are equal according to P.eq.

    Test your functor by applying it to structure IntEQ from lecture 15 to construct a structure named ListIntEQ. Show that that ListIntEQ works properly by building elements of type ListIntEQ and applying the function ListIntEQ.eq to the elements.

    signature EQ =
          sig
            type t
             val eq : t * t -> bool
          end;
      
    structure IntEQ : EQ = struct
          type t = int
          val eq : t*t->bool = op =
    end;
    
    functor ListEQ(P : EQ) : EQ = struct
      type t = P.t list
      fun eq([],[]) = true 
        | eq((fst1::rest1),(fst2::rest2)) = P.eq(fst1,fst2) andalso eq(rest1,rest2)
        | eq _ = false;
    end;
    
    structure IntListEQ : EQ = ListEQ(IntEQ);