[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Continuation examples (was Re: So, what the heck is a continuation anyway?)

   From: Luke Gorrie <luke@bluetail.com>
   Date: 11 Dec 2001 17:20:26 +0100
   Guy Steele - Sun Microsystems Labs <gls@labean.East.Sun.COM> writes:
   > Okay, just for fun, I upgraded the toy "FOO interpreter" of my
   > previous messages:
   Hello Guy, nice program :-)


   > 	((eq (first exp) 'LETREC)
   > 	 (let ((newenv (pairlis (mapcar #'first (second exp))
   > 				(make-list (length (second exp)))
   > 				env)))
   > 	   (@evletrec (second exp) newenv (third exp) newenv cont)))
   > (defun @evletrec (bindings slots body env cont)
   >   (cond ((null bindings) (@eval body env cont))
   > 	(t (@eval (second (first bindings)) env
   > 		  #'(lambda (fn)
   > 		      (rplacd (first slots) fn)  ;the side effect that "ties the 
   > 		      (@evletrec (rest bindings) (rest slots) body env 
   There's a bug here: evletrec assumes `slots' will be in the same order
   as `bindings', but `pairlis' can muck that up. On my computer with
   CLISP, the slots get reversed and the "evil" program becomes
   _diabolical_ (`last' is invoked first).
   Can be fixed by changing the `pairlis' in @eval to `pairlis-forward':
     (defun pairlis-forward (keys values alist)
       (append (mapcar #'cons keys values) alist))

Yes, good catch!  My face is red.

   > The lessons here are:
   > (1) LETREC is not that difficult to add.
   > (2) Even toy, throwaway interpreters are vulnerable to feature creep.
   (3) Even toy, throwaway interpreters have to be ported.

(4) I should read the manual!  I foolishly relied on my memory of pairlis;
but CLTL clearly states that "The new pairs may appear in the resulting
a-list in any order".

Another fix is to use name lookup rather than relying on order:

(defun @evletrec (bindings body env cont)
  (cond ((null bindings) (@eval body env cont))
	(t (@eval (second (first bindings)) env
		  #'(lambda (fn)
		      (rplacd (assoc (first (first bindings)) env) fn)
		      (@evletrec (rest bindings) body env cont))))))

and the code in @eval is now simply:

	((eq (first exp) 'LETREC)
	 (@evletrec (second exp)
		    (third exp)
		    (pairlis (mapcar #'first (second exp))
			     (make-list (length (second exp)))