Executive summary: I no longer want catch blocks to "daisy chain"
after a exception is thrown in a catch block. Thanks to everyone
who has helped me see the light on this.

Peter Scott wrote:
> 
> At 01:16 AM 8/16/00 -0600, Tony Olekshy wrote:
> >
> > The proposed omnibus Exceptions RFC uses the following three
> > rules to guide it while unwinding through the clauses of a
> > try statement.
> 
> Forgive me for eliding your explanation, but I find it a little
> opaque

Me too, that's why the subject contains the word "Towards".

> Let me advance a model which may be simpler.
> 
> 1. When an exception is thrown perl looks for the enclosing
>    try block; if there is none then program death ensues.
> 
> 2. If there is an enclosing try block perl goes through the
>    associated catch blocks in order.  If the catch criteria
>    succeed (the exception class matches one in a list, or a
>    catch expression evaluates to true, or the catch block
>    catches everything), the catch block is executed.
>
>    If the catch block throws an exception, it becomes the
>    'current' exception (with a link to the previous one),
>    otherwise there is no longer a current exception.
> 
> 3. Whether or not a catch block was executed, the finally
>    block is now executed if there is one. If the finally block
>    throws an exception, it becomes the 'current' exception
>    (with a link to the previous one if there was one).  At
>    this point, if there is a current exception, go to step 1.
> 
> This seems complete and IMHO easily understood.

Yes, that's much better.  Thanks Peter.  Here is a slightly
different version which is still slightly more complex, but
which allows me to more clearly illustrate the change I am
proposing.

  1. Whenever an exception is thrown it becomes the current
     exception (with a link to the previous one if there was one)
     and Perl looks for an enclosing try/catch/finally block.

     If there is no such enclosing block then program death ensues,
     otherwise Perl traps the exception and proceeds as per rule 2.

  2. The relevant try block's next associated catch or finally block
     is processed according to rules 3 and 4.  When there are no
     more blocks use rule 5.

  3. If the criteria for a catch succeed (an exception was thrown
     and either the exception class matches one in a list, or a
     catch expression evaluates to true without throwing, or the
     catch block catches all exceptions), the catch block is
     executed.

     If the catch block and its test expression do not throw then
     the current exception is cleared.  Otherwise, the exception is
     trapped and it becomes the 'current' exception (with a link to
     the previous one).

     Processing continues with rule 2.  [But see below --Tony]

  4. When a finally block is encountered it is executed.

     If the finally block throws an exception it is trapped and it
     becomes the 'current' exception (with a link to the previous
     one if there was one).

     Processing continues with rule 2.

  5. After the catch and finally blocks are processed, if there
     is a current exception then go to step 1.  Otherwise continue
     with the statement after the try statement.

My question is: where should processing continue after rule 3?

The version shown above produces the problem I described before:

> >     try { } except { } => catch { }
> >             except { } => catch { }
> >             catch  { }
> >
> > The potential problem is that if the first catch throws, then
> > the second except will be testing against the $@ from the catch,
> > not the $@ from the try.
> >
> > Is this a problem?
> 
> Yes, I think it breaks the intuitive model.

I do too, so the question is what to do about it. I propose changing
rule 3 to add the new paragraph marked with **:

  3. If the criteria for a catch succeed (an exception was thrown
     and either the exception class matches one in a list, or a
     catch expression evaluates to true without throwing, or the
     catch block catches all exceptions), the catch block is
     executed.

     If the catch block and its test expression do not throw then
     the current exception is cleared.  Otherwise, the exception is
     trapped and it becomes the 'current' exception (with a link to
     the previous one).

  ** If a catch test succeeds (or throws), then whether or not the
  ** catch block throws, any succeeding catch blocks up to the next
  ** finally block are skipped.

     Processing continues with rule 2.

This probably means that it should be a syntax error to have any
catch clause follow a non-conditional catch, because such a clause
could never be executed.

> throws() outside a try block are caught by the catch
> blocks of the next enclosing try block.  See above.

Not quite. Throws in a catch block (not a try block per se) still
have to be trapped in order to get to the finally block, if any.
I think you probably meant that, I'm just making it explicit.

The change I described above would get the effect we want, that is,
that throws in a catch block prevent subsequent catch blocks (up to
the next finally, if any) from being attempted, and after the finally
the throw from the catch is propagated.

To put it another way, catches don't "daisy chain" any more.  Once
a catch is triggered, subsequent catches (up to the next finally
block, if any) are ignored, even if the triggered catch threw.  I
think that gets rid of much of the complexity we have been trying
to avoid, and we can always use nested trys if we really need to.

> Let's leave $@ out of the discussion for now, since it implies a
> place to store an exception which is unnecessarily global.

I was just using it instead of $_[0] to try to make it clear that
I was talking about the current exception.  Perhaps I made it less
clear, eh?

Yours, &c, Tony Olekshy

Reply via email to