[comp.lang.scheme] catch/throw in scheme - how to ??

joe@erix.ericsson.se (Joe Armstrong) (06/10/91)

Could sombody who understands these things kindly explain how 
to do the equivalent of 'catch' and 'throw' in scheme?? -
can one use call/cc for such operations? - if so how?

Why? I want to do a non local return from a deep recursion 
when I dedect an error but not go to all the trouble of unwinding the
stack and building a lot of stuff which I'm going to junk anyway.

Joe Armstrong.

jinx@zurich.ai.mit.edu (Guillermo J. Rozas) (06/11/91)

In article <1991Jun10.124127.17926@eua.ericsson.se> joe@erix.ericsson.se (Joe Armstrong) writes:

   Path: ai-lab!mintaka!bloom-beacon!eru!hagbard!sunic!ericom!eua.ericsson.se!erix.ericsson.se!joe
   From: joe@erix.ericsson.se (Joe Armstrong)
   Newsgroups: comp.lang.scheme
   Date: 10 Jun 91 12:41:27 GMT
   Sender: news@eua.ericsson.se
   Organization: Ellemtel Telecom Systems Labs, Stockholm, Sweden
   Lines: 9
   Nntp-Posting-Host: gordons.eua.ericsson.se

   Could sombody who understands these things kindly explain how 
   to do the equivalent of 'catch' and 'throw' in scheme?? -
   can one use call/cc for such operations? - if so how?

   Why? I want to do a non local return from a deep recursion 
   when I dedect an error but not go to all the trouble of unwinding the
   stack and building a lot of stuff which I'm going to junk anyway.

   Joe Armstrong.

call-with-current-continuation (I hate the cutesy call/cc) can be
considered a generalization of catch and throw, although it has an
apparently more confusing interface.

You can easily rewrite programs that use catch and throw to use
call-with-current-continuation instead, but if you find the
catch/throw model more easily understood, we can define catch and
throw in terms of it.

Assuming that there are no CL-style multiple values (or that you are
not using them), and that the program will not error or be
interrupted, we can define catch an throw as macros in the following
way

(define-macro (catch tag . forms)
  `(catch-proc
    ,tag
    (lambda ()
      ,@forms)))

(define-macro (throw tag result-form)
  `(throw-proc
    ,tag
    (lambda ()
      ,result-form)))
	       
where the missing procedures are defined as

(define *catch-tags* '())

(define (catch-proc tag thunk)
  (let ((saved-tags *catch-tags*))
    (let ((value
	   (call-with-current-continuation
	    (lambda (cont)
	      (set! *catch-tags*
		    (cons (cons tag cont)
			  saved-tags))
	      (thunk)))))
      (set! *catch-tags* saved-tags)
      value)))

(define (throw-proc tag thunk)
  (let ((pair (assq tag *catch-tags*)))
    (if (not pair)
	(error "throw: No such tag" tag)
	((cdr pair) (thunk)))))

If you are using CL-style multiple values, and have fluid-let to boot,
then you can replace the definitions of catch-proc and throw-proc with
the following:

(define (catch-proc tag thunk)
  (apply values				; (values-list (call...))
   (call-with-current-continuation
    (lambda (cont)
      (fluid-let ((*catch-tags*
		   (cons (cons tag cont)
			 *catch-tags*)))
	(with-values thunk list))))))	; (multiple-values-list (thunk))

(define (throw-proc tag thunk)
  (let ((pair (assq tag *catch-tags*)))
    (if (not pair)
	(error "throw: No such tag" tag)
	(with-values thunk		; (multiple-value-call #'(lambda all ...)
	  (lambda all			;                      (thunk))
	    ((cdr pair) all))))))

kend@data.UUCP (Ken Dickey) (06/11/91)

joe@erix.ericsson.se (Joe Armstrong) writes:
>Could sombody who understands these things kindly explain how 
>to do the equivalent of 'catch' and 'throw' in scheme?? -
>can one use call/cc for such operations? - if so how?


Just use your local syntax system to define the syntactic transformation:

(catch . <body>) --> (call/cc (throw) <body>)

Then you can (throw <exp>) from within <body>.


-Ken Dickey			kend@data.uucp

janssen@parc.xerox.com (Bill Janssen) (06/11/91)

In article <JINX.91Jun10165336@chamarti.ai.mit.edu> jinx@zurich.ai.mit.edu (Guillermo J. Rozas) writes:
> Assuming that there are no CL-style multiple values (or that you are
> not using them), and that the program will not error or be
> interrupted, we can define catch an throw as macros in the following

...assuming you have macros :-)
-- 
 Bill Janssen        janssen@parc.xerox.com      (415) 494-4763
 Xerox Palo Alto Research Center
 3333 Coyote Hill Road, Palo Alto, California   94304

raible@nas.nasa.gov (Eric Raible) (06/12/91)

In article <502@data.UUCP> kend@data.UUCP (Ken Dickey) writes:

   From: kend@data.UUCP (Ken Dickey)
   Newsgroups: comp.lang.scheme
   Date: 10 Jun 91 23:20:22 GMT
   Organization: Microcosm, Beaverton, OR

   Just use your local syntax system to define the syntactic transformation:

   (catch . <body>) --> (call/cc (throw) <body>)

   Then you can (throw <exp>) from within <body>.


   -Ken Dickey			kend@data.uucp

Don't you mean this:

(catch . <body>) --> (call/cc (lambda (throw) <body>))

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/13/91)

In article <1991Jun10.124127.17926@eua.ericsson.se> joe@erix.ericsson.se (Joe Armstrong) writes:
> Could sombody who understands these things kindly explain how 
> to do the equivalent of 'catch' and 'throw' in scheme?? -
> can one use call/cc for such operations? - if so how?

In article <JINX.91Jun10165336@chamarti.ai.mit.edu>,
jinx@zurich.ai.mit.edu (Guillermo J. Rozas)
explained how to implement catch and throw on top of call/cc.

I suggest that it is simpler to use call-with-current-continuation directly.
For example, on p80 of Dybvig's book, we find

"Here is a more practical example, showing the use of call/cc to
provide a nonlocal exit from a loop.

    (define member
	(lambda (x ls)
	    (call/cc
		(lambda (break)
		    (do ((ls ls (cdr ls)))
			((null? ls) #f)
			(if (equal? x (car ls))
			    (break ls)))))))		"

(I have corrected two gratuitous incompatibilities with the standard.)

In general, the picture is
	(call-with-current-continuation (lambda (escape-function)
	    ;; any amount of your code goes here.
	    ;; When you want to return from the entire construct
	    ;; with value x, just call
	    (escape-function x)
	    ;; and that can go anywhere you like.
	    ))

The advantage of catch and throw is that a procedure can "throw" to
a "catch" which doesn't *lexically* enclose it.
-- 
Q:  What should I know about quicksort?   A:  That it is *slow*.
Q:  When should I use it?  A:  When you have only 256 words of main storage.

davis@barbes.ilog.fr (Harley Davis) (06/13/91)

In article <RAIBLE.91Jun12103833@wk43.nas.nasa.gov> raible@nas.nasa.gov (Eric Raible) writes:

   In article <502@data.UUCP> kend@data.UUCP (Ken Dickey) writes:

      Just use your local syntax system to define the syntactic transformation:

      (catch . <body>) --> (call/cc (throw) <body>)

      Then you can (throw <exp>) from within <body>.

      -Ken Dickey			kend@data.uucp

   Don't you mean this:

   (catch . <body>) --> (call/cc (lambda (throw) <body>))

In any case, this implements BLOCK and RETURN-FROM, not CATCH and
THROW.  CATCH/THROW requires a dynamic scope for the THROW procedure,
which is difficult (!) to implement just using CALL/CC.  The other
proposed solution was correct, though, if not very efficient.

-- Harley Davis
--
------------------------------------------------------------------------------
nom: Harley Davis			ILOG S.A.
net: davis@ilog.fr			2 Avenue Gallie'ni, BP 85
tel: (33 1) 46 63 66 66			94253 Gentilly Cedex, France

markf@zurich.ai.mit.edu (Mark Friedman) (06/14/91)

In article <6246@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:

   In article <1991Jun10.124127.17926@eua.ericsson.se>
joe@erix.ericsson.se (Joe Armstrong) writes:

   > Could sombody who understands these things kindly explain how 
   > to do the equivalent of 'catch' and 'throw' in scheme?? -
   > can one use call/cc for such operations? - if so how?

   In article <JINX.91Jun10165336@chamarti.ai.mit.edu>,
   jinx@zurich.ai.mit.edu (Guillermo J. Rozas)
   explained how to implement catch and throw on top of call/cc.

   I suggest that it is simpler to use call-with-current-continuation directly.

This may be true, but I think the point was that Joe Armstrong knew
what catch and throw did and wanted to see how they could be
implemented in terms of call-with-current-continuation.

   The advantage of catch and throw is that a procedure can "throw" to
   a "catch" which doesn't *lexically* enclose it.

I can apply any escape procedure (the packaged-up form of a
continuation) anywhere. The fact that catch and throw maintain a
dynamic mapping of catch-tags to continuations can be convenient
though. The next version of MIT Scheme will have Common Lisp style
restarts, which provide that convenience plus the ability to inspect
the mappings and to see dynamically shadowed mappings.

-Mark
--

Mark Friedman
MIT Artificial Intelligence Lab
545 Technology Sq.
Cambridge, Ma. 02139

markf@zurich.ai.mit.edu