Tony Olekshy wrote:

> Glenn Linderman wrote:
> >
> > Perl 5 doesn't have exceptions, which is precisely how it avoids this
> > problem.
>
> Perl 5 has exceptions.  my $x = 0; my $y = 1 / $x;  It avoids nothing,
> which is we are always mixing return-based and exception-based failure
> signalling.  This is not going to go away, and everyone likes that.

Well.  I think this is the root of a lot of the misunderstandings about
exception mechanisms.  I started figuring this out when trying to figure
out what Tom was asking when he reminded us about fatal.pm.  And also some
of what Marcus Peter was proposing, at least the way he worded it, helped
me down this path.  And now this remark.  And everyone who suggested that
"not checking the return code" meant "ignoring errors" were clearly only
ignoring non-fatal errors, as you pointed out to several of them, Tony...
but I posit that is exactly what they were trying to do! (or not do!)

Perl today has two mechanisms for dealing with errors, or maybe three,
depending on how you count...

1) return a failure indicator, and store error information in punctuation
variables.
2a) die, due to a fatal failure
2b) eval/die combination, to trap fatal errors

The way I count, 2a & 2b are just variations on the same theme, so I find
two mechanisms.

The way I read the history (but maybe someone that was there can correct
me), 2b allows fatal errors to be trapped, and there was no other way to
trap errors of any other kind, so 2b got overloaded to be called the Perl
exception mechanism... eval will trap/report the fatal errors, and you can
use the fatal error invocation mechanism, die, with it to build an
exception mechanism.

This is fine, sort of, except that it has undesirable side effects.

Let me define a few more terms before continuing.

1) Return error mechanism - some combination of return codes, returned
failure indicators, punctuation variables, and other ad hoc parameters and
return values that are used to indicate failure of the requested function.

2) fatal error mechanism - Perl's present die/eval, when used for reporting
fatal errors

3) exception mechanism - A mechanism for reporting non-fatal errors via a
structured mechanism that can  conveniently exit multiple scopes.  While
many Perl modules are built around overloading the fatal error mechanism
for exceptions, I'd like, for the purposes of this EMail at least, to
define it as a separate mechanism.

RFC 119 views item 3 as something to be added to Perl, to provide a
technique for reporting non-fatal errors in a structured manner.

I'm now reaching the conclusion that RFC 88 is apparently building more
mechanism around item 2 to make it prettier for use as a general exception
mechanism.  If so, I don't think that is a good idea.  Separate mechanisms
should be used for clarity and compatibility.  But on the other hand, if
you really want to trap both fatal and non-fatal errors, doing it via a
single general exception mechanism is nice.  How can we achieve both?  Read
on.

The example above, with the divide by zero error, is a fine example of
appropriate use of a fatal error handling mechanism.  99% of the time, you
don't want to think about it in writing scripts, and the default failure
mode is great.

However, when you start adding non-fatal errors to the mix, using the same
mechanism to trap both fatal and non-fatal errors is problematical: use of
a sub or module that returns non-fatal errors via the fatal error mechanism
then forces you to distinguish and handle fatal errors that might occur via
that same mechanism, because a catch-all (catch without a class or
expression based selector) might not really want to catch fatal errors, yet
you might not want to explicitly write handlers everywhere to deal with
fatal errors either, to get them excluded from the catch-all.

I posit that if all three mechanisms are available, it is possible to
convert between them, as follows.

To convert return error mechanism to fatal error mechanism, use Fatal.pm.
It doesn't handle all the cases, but handles quite a few.  That's for core
functions.  For modules, write a wrapper module that converts the API.

To convert return error mechanism to exception handling mechanism, use
Throw_non_fatal.pm, which is a clone of Fatal.pm which does a throw instead
of a die for the non-fatal errors.  Again, it wouldn't handle all the
cases.  Again, this is for core functions; modules can be wrapped.

To convert fatal errors into exception handling, write a module
Throw_fatal.pm based on overriding die that could choose particular fatal
errors, and execute a throw instead.  This module, in Perl5, would probably
have to take a list of patterns to match against the fatal error texts, or
a hash keyed by the fatal error text; maybe Perl6 could implement unique
numeric codes for each fatal error, and the codes to handle could be passed
instead as a list of ranges.

To convert non-fatal errors into fatal errors: don't write catch clauses,
as the default catch clause in the outer scope will die.  Or write module
wrappers which use die in the catch clauses.

I see no benefit to converting truly fatal errors from core functions into
return codes, except to ignore them.  One could write a Ignore_fatal.pm
module to do that, but it could only apply to functions that already return
some other errors via a return code.  It wouldn't be capable of trapping
divide by zero in straight code, for example.  Or one could write a module
which overrides die to completely ignore certain fatal errors, but I think
we are talking foolishness.  Except for user-coded dies which attempt to
use the fatal error mechanism as an exception mechanism... because there
was no other mechanism.  So one problem is knowing how to match all the
exception strings of interest from all the modules you might use.  So if
you can do that, you write a wrapper module which "eval"s your module with
user coded "non-fatal" dies, and use return codes.  Because it is a wrapper
module, it can change the calling sequence as necessary to provide for a
return code.

Similarly, converting fatal errors from user coded dies to exceptions can
be done with a wrapper module which throws.

OK, so if we have three mechanisms, it is possible to convert many of the
uses of one mechanism to uses of the other mechanism, via wrapper
subs/modules.

But if we want to be able to funnel all the error handling through a single
mechanism, we could, on a module by module basis:

1) Select a preferred mechanism or combination of mechanisms.

2) Write wrapper subs/modules for any subs/modules we wish to use that do
not use our preferred mechanism or combination of mechanisms.  Wrappers for
many builtins can be done easily for us via Fatal.pm and my proposed
Throw_non_fatal.pm modules.

3) Write the code in your preferred style.

Clearly the wrapper modules are reusable, and can be shared among scripts,
or even with others, possibly even via CPAN.


OK, so what is the undesirable side effect of using die for non-fatal
errors?  They cannot later be distinguished from truly fatal errors.  If
they could be distinguished, then we could build a single mechanism for
both fatal and non-fatal errors, and appropriate techniques could be added
to handle the various cases.  For example, if truly fatal errors could be
distinguished, one could declare that a catch-all clause "catch { ... }"
would not catch fatal errors, that it would be necessary to specifically
list that you want to catch a fatal error... maybe "catch Fatal { ... }",
or the like.

> The discussion here is about how to do the case where you do want to
> do exception-based failure signalling.  On that there seems to be
> general agreement.

Well, yes, that's the main discussion.  But there are side forays into
ignoring errors, and turning exceptions on and off, and I think some of
that is due to the two different classes of errors that exist, fatal, and
non-fatal, the fact that it seems that the default handling for them should
be different, but that RFC 88 merges them together.

So far RFC 119 doesn't merge them together, but it kind of abandons all the
existing code that is built using eval/die for non-fatal errors until such
time as it can get converted.

Perhaps p52p6 should have an option to automatically convert die/eval to
catch/throw.  An option, because it won't be able to tell the difference
between fatal and non-fatal either, so the user would have to choose which
set is smaller, to convert by hand after p52p6 is done.

> Yours, &c, Tony Olekshy

--
Glenn
=====
There  are two kinds of people, those
who finish  what they start,  and  so
on...                 -- Robert Byrne



_____NetZero Free Internet Access and Email______
   http://www.netzero.net/download/index.html

Reply via email to