Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

throw and catch...

4 views
Skip to first unread message

David Bakhash

unread,
Jun 20, 1999, 3:00:00 AM6/20/99
to
My question is about throw/catch. It seems that you can only `throw' a tag
which is somehow called down-the-line of the `catch'. By this, I mean that
the code with the `throw' must somehow be called (directly or indirectly) from
code in the corresponding `catch' form. What I want to do is a bit
different, and throw/catch do not seem to work for it.

First off, what I'm doing is non-standard (it uses multiprocessing). I have a
loop which runs forever. I would like to be able to break out of it at any
time, execute whatever protected forms were necessary, and then get out of
there. But I want to be able to do it from a remote part of the Lisp world
(i.e. a totally separate thread). Basically, like throw and catch, but not so
strict on the lexical/dynamic restrictions.

The thing that might be missing from my understanding is "environment". I
never understood exactly how environments are used in Lisp. If this is the key
to what I'm missing, I'd appreciate help.

dave

Erik Naggum

unread,
Jun 21, 1999, 3:00:00 AM6/21/99
to
* David Bakhash <ca...@acs.bu.edu>

| First off, what I'm doing is non-standard (it uses multiprocessing). I
| have a loop which runs forever. I would like to be able to break out of
| it at any time, execute whatever protected forms were necessary, and then
| get out of there. But I want to be able to do it from a remote part of
| the Lisp world (i.e. a totally separate thread). Basically, like throw
| and catch, but not so strict on the lexical/dynamic restrictions.

I do this all the time. here's how. in process A, you have:

(catch 'interrupt
(loop ...))

you need a function INTERRUPT

(defun interrupt (&optional value)
(ignore-errors (throw 'interrupt value)))

now you can do

(process-interrupt <process-A> #'interrupt <value>)

assuming, of course, you have something like Symbolics' multiprocessing,
such as supported by Allegro CL. note that INTERRUPT actually executes
in process A, i.e., inside the proper dynamic environment. (you want the
IGNORE-ERRORS because you have no idea what process A is doing when it is
asked to run this function, and you might execute entirely unexpected
error handlers if you try to throw to a tag not in scope at the time.)

#:Erik
--
@1999-07-22T00:37:33Z -- pi billion seconds since the turn of the century

Kent M Pitman

unread,
Jun 21, 1999, 3:00:00 AM6/21/99
to
Erik Naggum <er...@naggum.no> writes:

> I do this all the time. here's how. in process A, you have:
>
> (catch 'interrupt
> (loop ...))
>
> you need a function INTERRUPT
>
> (defun interrupt (&optional value)
> (ignore-errors (throw 'interrupt value)))
>
> now you can do
>
> (process-interrupt <process-A> #'interrupt <value>)
>
> assuming, of course, you have something like Symbolics' multiprocessing,
> such as supported by Allegro CL. note that INTERRUPT actually executes
> in process A, i.e., inside the proper dynamic environment. (you want the
> IGNORE-ERRORS because you have no idea what process A is doing when it is
> asked to run this function, and you might execute entirely unexpected
> error handlers if you try to throw to a tag not in scope at the time.)

It's a little more elegant if you use restarts instead of catch/throw,
though the effect is the same. (Restarts are implemented with catch/throw,
after all.) The difference is that restarts offer an introspective
mechanism that allows you to first determine that the throw tag [restart]
is not present and not to try throwing. That means you don't have to
do the throw in an IGNORE-ERRORS, and it also allows you to choose among
several possible return points in more elaborate situations.

(defun compute-something-interruptible ()
(with-simple-restart (dismiss "Dismiss pending computation.")
(loop ...)))

(defun interrupt ()
(let ((r (find-restart 'dismiss)))
(when r (invoke-restart r))))

(process-interrupt <process-to-interrupt> #'interrupt)

It can be done with values, too, but I did it without because I'm in a
hurry to rush out the door and anyway I thought it would make the
shape of the code clearer. To use values, you just use invoke-restart
with an extra arg and change the with-simple-restart to a restart-case.
The syntax for restart-case is a lot more general and is documented
in CLHS.

David Bakhash

unread,
Jun 21, 1999, 3:00:00 AM6/21/99
to
What Eric wrote was exactly what I was looking for. And what KMP wrote is
probably good for the future, for more robust code.

thanks, very much

dave

0 new messages