From: David Bakhash
Subject: throw and catch...
Date: 
Message-ID: <cxjiu8iv501.fsf@acs5.bu.edu>
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

From: Erik Naggum
Subject: Re: throw and catch...
Date: 
Message-ID: <3138962515282167@naggum.no>
* David Bakhash <·····@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
From: Kent M Pitman
Subject: Re: throw and catch...
Date: 
Message-ID: <sfwg13locdf.fsf@world.std.com>
Erik Naggum <····@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.
From: David Bakhash
Subject: Re: throw and catch...
Date: 
Message-ID: <cxjhfo1v7vt.fsf@acs5.bu.edu>
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