Peter Scott wrote:
> 
> Tony Olekshy wrote:
> >
> >     trap { $@->{message} =~ /divide by 0/ } catch { ... }
> 
> I don't think you need another keyword here.  Just support an
> expression argument to catch and you can do
> 
>       catch $@->{message} =~ /divide by 0/  { ... }
> 
> If it needs to be more complicated, use 'do'; that should
> still make it parseable.  At the very worst it may need a
> comma the way map uses it, which we can write as =>.  But I
> don't think that'll be needed.

I do find that a bit hard to parse, but it probably wouldn't be
too bad in practice.  Let's go with it, and then maybe you'll
let me keep finally instead of continue ;-)

I've also added a new ISSUES + Syntax entry, which contains:

    If C<catch EXPR { ... }> proves to be difficult for Perl
    to parse, then the form C<trap { EXPR } catch { ... }> is
    a possible alternative (based on earlier versions of this
    RFC).

> Note: I used to favor $_[0] for the exception, but I'm being
> won over to $@.  As long as it doesn't cause problems with
> external interfaces.  Might mention the $_[0] idea in a
> footnote.

Done.

> >     finally { ... }
> 
> Still want this to be "continue"; let's mention both.

It will be included in the alternative keywords table I'm
working on.

> > Any exceptions that are raised within an enclosing try,
> > catch, trap, or finally block (anywhere up the subroutine
> > call stack) are trapped and processed according to the
> > semantics described in this RFC.
> 
> Maybe it's just me, but I think of 'up' the stack as being my
> callers, and 'down' being my callees.

I had to laugh.  You read the parenthetical as applying to
raising, and I wrote it as applying to the enclosing block!
I've changed it to:

    Any exceptions that are raised within an enclosing try,
    catch, trap, or finally block, where the enclosing block
    can be located anywhere up the subroutine call stack, are
    trapped and processed according to the semantics described
    in this RFC.

> >exception 'Error::DB::Foo';
> >
> >     If the given name matches /::/, something like this
> >     happens:
> >
> >         @Error::DB::Foo::ISA = 'Error::DB';
> >
> >     If the given name does not match /::/ (say it's just
> >     'DB'), this happens instead:
> >
> >         @DB::ISA = 'Exception';
> 
> I think you want to s/Error/Exception/ above.  Then the rule is
> simpler.  Require all exceptions to be subclasses of 'Exception'
> for clarity.

If I did it that way, then to create an exception error class
derived from Exception, I'd have to call it Exception::Error.
This would mean testing for Exeption::Error::IO::File::Open.

The way it's written above, C<exception 'Error'> makes Error a
derivative of Exception, but it's name is Error.  It's a way of
saying that the base class for exception objects is *always*
Exception, even if their name doesn't start with Exception::

> > throw Error::DB => "a message", tag => "ABC.1234", ... ;
> 
> Suggest dropping the =>.  Then throw can be implemented as an
> object method and this is just the indirect object syntax.

I forgot. Earlier yesterday I still needed a subroutine, but
now that I came with the inheritance trick described above,
I don't.  It means we can't just throw to reraise, but as you
mention below, we can just throw $@;  Throw will be a method of
the Exception class and of Exception objects in the next draft.

> Do you really want to leave out the "message =>" there?  IMHO
> it looks confusing to tie the class to the message and leave
> the message attribute name out.  So I suggest
> 
>    throw Error::DB (message => "a message", tag => "ABC.1234");
> 
> except that my vote is for "code" instead of "tag".

We write a lot of simple throws, most of which would look like
this with the new mechanism as proposed so far:

    throw MyDB "ABC.1234: Some message about what went wrong.";

Without the techniques and hooks I've described in 88v2d2, I'd
have to write the following, which has a much lower signal to
noise ratio.

    throw Exception::MyDB tag => "ABC.1234",
        message => "Some message about what went wrong.";

Even though I want non-local flow-control to stand out with new
keywords, I don't what it to obfuscate and overwhelm the source
code for the algorithms that use it.

And, I really want to encourage people to put an annotating message
on *all* the exceptions they raise; I don't want to make it harder by
requiring the minimal canonical throw form to always have to contain
the message => tokens.

> >     A throw with no arguments is syntactic sugar for re-raising
> >     the current exception.  If there is no current exception,
> >     an anonymous Exception is manufactured first.
> 
> Um, I don't like that.  What's an anonymous Exception?  What
> would its attributes be?

An anonymous exception is, literally, the result of a
non-subclassed, non-parameterized, Exception->New().
Under ISSUES + Exception Base Class, RFC 88 v2d1 says:

    Default values for tag and severity?

> I think it's easier to require them to "throw $@" if they want
> to rethrow, so you're not forced into this anonymous exception
> thing.

Done.  But one can still say C<throw Exception;> so the issues
don't completely go away.

> >catch { ... }
> >
> >     Otherwise, it is semantic sugar for trap { 1 } catch { ...
> >     }.
> 
> AFAIK, only syntax is sweetened.  Semantics are inherently
> low-cal.

Fixed.  Thanks.  I've seen some pretty fat semantics though ;-)

> >catch "Error::DB" { ... }
> 
> I want to be able to drop the quotes.  With 'catch' being a
> full-blown keyword, this is possible.

I wanted to be able to say things like:

        @whatToCatch = SelectExceptionClasses();

        try { } catch \@whatToCatch { ... }

If course, now I can just use

    catch grep( $@->isa($_), @whatToCatch ) catch { ... }

in the rare case where I might actually find such a use, so
I've modified 88v2d2 as you suggest.  I've also taken the
[]s and 's off the catch LIST BLOCK form.

> >die
> >
> >     If argument isa "Exception", raise it as the new
> >     exception and die in the fashion that Perl 5 does.
> >
> >     If argument isa "UNIVERSAL", wrap it in a new Exception
> >     (using the "object" instance variable), and raise that
> >     instead.
> 
> Hmm... what is the benefit of being able to throw objects that
> aren't exceptions?

The question is, what happens when someone throws an object
that isn't an Exception?  Since exception handling now depends
on it, we have to enforce it.

> >    When an exception $e is raised, the following is
> >    automatically done:
> >
> >             $e->{link} = $@;  $@ = $e;
> 
> Only if $@ was defined, I presume.

Um, $e->{link} = undef is ok with me.

> >   1. Whenever an exception is raised Perl looks for an
> >      enclosing try/catch/finally block.
> >
> >      If such a block is found Perl traps the exception and
> >      proceeds as per rule 2, otherwise program shutdown is
> >      initiated.
> 
> I wonder if we should anticipate the possibility of
> $SIG{__DIE__} surviving... and saying that at this point
> it could get its hands on the exception.

Not in Rule 1, it will confuse the issue.  I've added the
following to ISSUES + $SIG{__DIE__}.

    If $SIG{__DIE__} is not removed, it should be invoked at
    the point of the phrase "program shutdown is initiated"
    in Rule 1, not at the time an exception is raised.

> >=head2 Built-In Exception Base Class
> >
> >In addition to the built-in Exception class described below, a
> >built-in Error class is defined.  The Error class inherits from
> >Exception.  Internal Perl assertion failures are instances of
> >exception classes that inherit from the Error class.  This
> >results in code like C< catch "Error::IO::File" { } >.  It
> >also allows simple anonymous error exceptions to be raised with
> >a form like this:
> >
> >     throw Error => "message", severity => "Fatal";
> 
> I don't see what you mean by 'anonymous' here.

That "anonymous" has been removed.

> I also think there should be exactly one exception class
> hierarchy.

There is one exception class hierarchy, rooted in Exception.
Perl classes don't have to expose the underlying hierarchy in
their name; consider:

        package Error;  @ISA = 'Exception';

It's just sometimes convenient to do so.  Exception::Error is
too long to be convenient when I want to say throw Error;

> >trace
> >
> >     A listref containing a snapshot of the call-stack as at
> >     the time the exception is first raised.  The array
> >     contains hashes (one per call stack level), each
> >     containing one key value pair for each snapshot value at
> >     that level.  Here are some examples:
> >
> >             $e->{trace}->[0]->{file}
> >             $e->{trace}->[0]->{line}
> >             $e->{trace}->[0]->{sub}
> >
> >     This snapshot is set up by the "snapshot" method, so
> >     that derived classes that don't want this overhead can
> >     override the method.
> 
> I know I was pushing for this, but Ive seen the light that in
> fact caller() should be doing this for us.  Can you see any
> reason that won't do?

Here I don't understand.  How can caller give us the raise-time
call stack at catch time (at the latter time, it can only give
us the catch-time call stack).  The functionality of caller() is
exactly what's used by the snapshot method to populate the trace
instance variable, but said snapshot has to be taken early and
held for possible historic debugging use.  Usually I want to
know where we were when things went wrong, not where we realized
that (and in the later case, caller()s output is current).

Am I missing something?

> >overload '""' => sub {
> >
> >     my $t = exists $_[0]->{tag} ? $_[0]->{tag} . ": " : "";
> >
> >     exists $_[0]->{severity} and $t .= "($_[0]->{severity}) ";
> >
> >     $t .= $_[0]->{message} =~ /\S/
> >         ? $_[0]->{message} : "anonymous exception.";
> >     }
> 
> I think we should just be defining semantics here, not supplying
> code.

Draft defense ;-)  I've made a note not to forget to fix this.

> >=head1 EXAMPLES
> >
> >The following three examples conver the most common uses of try,
> >catch, and finally.
> >
> >     open(*F, ">foo");  try { ... } finally { close F; }
> 
> Why isn't the open inside the try block?  Don't you want to
> see if it succeeded?

Because then finally will attempt to close F even if open F
fails.  In this case I don't want to see if it succeeded, I just
want to make sure I close it whether or not anything went wrong
since I successfully opened it.  Hmm, I'm assuming open throws
error exceptions in this example, perhaps I should make that
more clear, so I changed the draft to read:

    if (open(*F, ">foo")) { try { ... } finally { close F; } }

> See my earlier comments about all linked blocks sharing the
> same lexical scope, btw.

I'd rather not introduce that complexity into the RFC.  It's
already hard enough to keep track of lexical scope in nested
closures; I don't want to add nested combinations of trys
(possibly containing closures) into the mix.  I've added an
ISSUE to this effect to the RFC.

> > Keyword Names
> >
> >     RFC 88 only introduces the try, throw, trap, catch,
> >     finally, and exception keywords, which are all
> >     traditionally related to exception handling.  Also,
> >     "throw" was chosen because of it neutral connotation
> >     (unlike "fail" for example), because exceptions do not
> >     necessarily encapsulate a negative.
> 
> I think throw and exception only need to be functions.

Depending on what extensions are made to Perl 6's parser
extensibility, they may all be functions ;-)  You are right,
but I'd rather consider them as a batch of new words, and avoid
that detail.

> >    because the author is of the opinion that overloading
> >    else and continue with unwind semantics not traditionally
> >    associated with else and continue can be confusing,
> >    especially when intermixed with local flow-control forms
> >    of else and continue, or when an "else throw" is
> >    forgotten on a switch that needs to re-throw.
> >
> >    The "try" is not necessarily for Perl's sake.  It's for
> >    the programmer's sake.  It says, watch out, some sort of
> >    non-local flow control is going on here.  It signals
> >    intent to deal with action at a distance (unwinding
> >    semantics).
> 
> Well said.

Thanks. How much would you pay for this--but wait, there's more.
Here's a quote from the Encyclopaedia of Software Engineering
that's going into 88v2d2 (check there for the full reference).

    Among the features offered by programming languages to
    support exception handling are the following.

     1. The ability to distinguish the normal control flow from
        the exception handling flow to make the structure of the
        program clear.

     [...]

    Most early programming languages do not provide specific
    features for exception handling, but rather use the normal
    constructs to implement it. [...]  Obviously this and other
    ad hoc methods to not satisfy the requirements listed above.

> >    How to arrange the exception class hierarchy for the Perl
> >    core?
> 
> This is the province of RFC 80.  I can just remove the stuff
> that's already been subsumed in this draft.

As noted in 88v2d1, I haven't got to the impact statements for
the other RFCs yet.

Yours, &c, Tony Olekshy

Reply via email to