CS 131 Homework 9

Due by the beginning of class Wednesday, April 12
  1. A recursive version of Ackermann's function is given below. Rewrite ackermann and superorder in full continuation-passing style (i.e., the functions returned by superorder-cps should also be in CPS). Calling (ackermann-cps 3 (lambda (v) (pretty-print v) 'done)) should print 27.

    (define ackermann
      (lambda (n)
        ((superorder n) n n)))
    
    (define superorder
      (lambda (n)
        (cond
          ((= n 1) (lambda (x y) (+ x y)))
          ((= n 2) (lambda (x y) (* x y)))
          (else (lambda (x y)
    	      (if (= y 0)
    		1
    		((superorder (- n 1)) x ((superorder n) x (- y 1)))))))))
    
  2. Study the CPS interpreter we developed in class this week:

    Use this code as your starting point for the next two problems.

  3. Add support for escaping functions to the interpreter, with the following syntax:

    (escfun (<var> ...) -> <body> <body> ...)
    

    An escaping function is just like a regular function, except that when it is applied, it returns the resulting value to top level and aborts any remaining computation in progress, without exiting from the interpreter. For example:

    > (start)
    
    Welcome to the CS 131 Ploy interpreter
    
    ==> (let ((square (escfun (n) -> (n * n))))
          (100 * (2 + (square 5))))
    25
    ==>
    
  4. If the interpreter encounters an unbound variable, Scheme's error handler is called, which terminates everything. For example:

    > (start)
    
    Welcome to the CS 131 Ploy interpreter
    
    ==> (list 1 2 3 a)
    
    Error in empty-environment: variable a is unbound.
    Type (debug) to enter the debugger.
    > 
    

    What we really want is for the interpreter to abort the computation with an appropriate error message and return to top level, without terminating the read-eval-print loop. Suppose we redefine apply-env so that instead of calling error, the empty environment invokes the REP-k continuation, like abort does:

    (define apply-env
      (lambda (this-env symbol)
        (cases environment this-env
          (empty-environment () (REP-k `(error: ,symbol is unbound)))
          ...)))
    
    > (start)
    
    Welcome to the CS 131 Ploy interpreter
    
    ==> (list 1 2 3 a)
    (error: a is unbound)
    ==> (2 + 3)
    5
    ==> 
    

    This seems to work fine. However, a strange thing happens next if we try to exit the system:

    ==> (exit)
    (1 2 3 goodbye)
    ==>
    

    What's going on here? In a comment in your code, explain in detail exactly what causes this behavior. To fix the problem, you'll need to convert lookup-value, lookup-ref, lookup-ref-in-first-frame, and apply-env to CPS. Changing the other functions in environments.ss is not necessary. You'll also need to rewrite the calls to these functions wherever they appear in the code, including in the parser.

  5. CPS versions of the factorial and fibonacci functions are shown below. In this code, continuations are represented as Scheme functions. Transform this code to first-order tail form.

    (define fact
      (lambda (n)
        (fact-cps n (lambda (v) v))))
    
    (define fact-cps
      (lambda (n k)
        (if (= n 0)
          (k 1)
          (fact-cps (- n 1) (lambda (v) (k (* n v)))))))
    
    (define fib
      (lambda (n)
        (fib-cps n (lambda (v) v))))
    
    (define fib-cps
      (lambda (n k)
        (cond
          ((= n 0) (k 0))
          ((= n 1) (k 1))
          (else (fib-cps (- n 1)
    	      (lambda (v1)
    		(fib-cps (- n 2)
    		  (lambda (v2)
    		    (k (+ v1 v2))))))))))
    

Extra Credit

Turning in your homework

Put your code from parts 1 and 5 into a single Scheme file named assign9.ss. Files to submit:

For the extra credit problems, put your CPS challenge answer in a file named ec-challenge.ploy and your breakpoint interpreter code in separate files named ec-interpreter.ss, ec-parser.ss, and ec-environments.ss. Files to submit:

If you have questions about anything, just ask!