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

A concept for Exceptions

0 views
Skip to first unread message

mosul...@crtinc.com

unread,
Oct 15, 2002, 2:33:28 PM10/15/02
to perl6-l...@perl.org
A brainstorm for your enjoyment, perusal, and general discussion...


SUMMARY

A proposal for an extension to the usual exception handling concept. The
concept detailed here provides a mechanism for handling exceptions in one
of three ways: changing the values being evaluated, setting the result of
the entire expression, or throwing an exception as usual.

JUSTIFICATION

Why do I want to augment how exceptions are handled? Is everything about
exceptions already invented?

The answer is that I hate how Java handles exceptions. In Java it seems
like every time you want to do something, you have to surround it with
exception handlers. I find that in Java my code becomes buried in
exceptions. Yes, the current every-block-is-a-try-block greatly helps this
problem, but there's still the looming prospect of having to write
mountains of CATCH blocks to handle every problem.

What I propose here is a system that provides a technique for actually
fixing the problems on the fly, instead of having to hand code error
handling every step of the way. Furthermore, the techniques described here
provide a way to make certain behaviors modifiable, so that everybody gets
their own favorite flavor of ice cream, so to speak. This concept was
inspired by the variety of preferences about how Perl6 should handle 1/0.

Please note that this is not a proposal for *changing* how exceptions are
handled, it's merely an *addition* to the standard techniques.


EXCEPTIONCREATOR CLASS

Each block gets a reference to an ExceptionCreator object. By default,
blocks inherit their parent's reference, so we won't have to be moving and
creating references a lot. When the interpretor gets to an error, it calls
the ExceptionCreator's standard method for handling that specific type of
exception: divByZero(), stringConcatenationUsingUndef(), whatever. The
method for that type of exception calls the constructor of an Exception
class that is devoted to handling just that type of exception. The
Exception's constructor method, described in more detail on the next
section, determines how the exception is handled.

To change how certain exceptions behave, a block simply changes the methods
of the existing ExceptionCreator to point to other subroutines. This
approach allows for an ala carte style of exception configuration. Blocks
can (through a module that makes this sort of thing easy), clone the
ExceptionCreator object, then change just the methods that are desired. A
reference to that new object is passed down the line to child blocks.

You can also set the "default" ExceptionCreator reference, i.e. the
ExceptionCreator used by default by all blocks in the entire program. By
doing so, you can set how certain exceptions are handled by modules that
you use but didn't write. For example, suppose I want all string
concatenations using undef to be fatal (and I do). I simply override the
default ExceptionCreator's concatUndef() method to something more fatal.

The following are some of the standard exceptions that the interpretor
might throw. I'm sure there a more. These are just the ones that come to
mind.

- divide by zero
- numeric operation on non-numeric value
- string concatenation using undef
- use of tainted data in protected operation
- unsuccesful attempt to open file

Modules that provide alternate exception handlers should supply static
methods for making the change to a given block. For example, to have div
by zero evaluate to Inf instead of a fatal exception, you could simply add
this code to your block:

use Math::Inf::Exception;

and voila, the import routine sets your block's ExceptionCreator for
handling div by zero errors.


EXCEPTION CLASS

Every type of standard exception has its own class. The static constructor
method, new, accepts one argument and returns one of three possible values.

The input is a reference to the object that caused the exception. This is
an optional argument in case no specific object threw the exception. An
example of an object that threw an exception would the undef object used in
a string concatenation.

The constructor returns one of three values: an exception object, the
DO_OVER constant, or the EXPRESSION constant.

If an exception object is returned, that means that the interpretor should
immediately exit the block, throwing the exception to that block's CATCH
block, or its parent's CATCH block, and so on outward until somebody
catches it. You know the routine.

DO_OVER means to reevaluate the expression because the reference to the
offending variable has been set to something else. For example, the undef
might have been changed to an empty string. The interpretor will only call
each type of exception once for each type of object. If the exception
constructor just sets the value to another undef, the interpretor will make
a nasty gesture are the handler, so "enough of you", and throw its own
UnhandledException exception.

It must be noted that changing the value of the offending object does NOT
mean that the variable in the block is permanently changed. The value is
only changed for that particular evaluation of the variable. The
constructor simply changes the reference from one object to another, so we
don't need to go around cloning objects, we just move references around.

EXPRESSION means that the interpretor should stop trying to evaluate the
expression, and should use the value returned as the second value in the
return array (or however we want to move that value around). For example,
a div by zero error would normally return an exception that must be caught
somewhere. Programmers who want 1/0 to evaluate to Inf could use an
exception constructor that sets the expression to an Inf object. The Inf
object would in turn overload all the math operators so the, for example, 1
+ Inf is Inf. Note that "the entire expression" is only the three values
involved in the division, not the entire mathematical expression. So, for
example, this full expression:

(1/0) + 3

would trigger the error just for the (1/0) part, which would be evaluated
to Inf, thus turning the full expression into:

Inf + 3

at which point the Inf's operator overloading kicks in and returns Inf.


VARIATIONS ON THE THEME

I list here some variations on the ideas discussed above, because some of
these ideas seem good but don't quite fit into the scheme described.
Perhaps you can shoehorn them in.

I'm still a little hesitant about this "offending object" concept. Perhaps
instead, each class could get an array of all the objects and operators in
the expression being evaluated. Again, that doesn't necessarily mean an
entire mathematical expression, it would just be the subset be evaluated.
So, for example, the div by zero handler would get references to the
objects containing [1, '/', 0] in this expression:

(1/0) + 3

The Exception could have a static method called TriggerException that is a
step above actually creating and throwing an exception. TriggerException
does what the interpretor does: detect if the ExceptionCreator has a method
for a given class of Exception. If TriggerException returns false, then
the module should just throw an exception as usual. By using
TriggerException, a module can allow users to handle their exceptions in
the manner described above. For example, suppose a DBI class used
TriggerException to indicate that a database connection has died. The
default behavior would be to throw a DbDied exception and exit the block.
However, a corporate programmer could set up a special handler that tries
to reconnect to that or another database, and if it finds an alternate
connection, reaches into the calling block (constructors could get hooks
into the calling block), changes the variable that holds the database
connection, and calls a DO_OVER. In this way the exception handler becomes
more than just a way to report errors: it gets actively involved in fixing
them. ExceptionCreator also becomes a more extensible concept, allowing
any type of Exception to be handled dynamically.


-miko

--------------------------------------------------------------------
mail2web - Check your email from the web at
http://mail2web.com/ .


Luke Palmer

unread,
Oct 15, 2002, 3:14:27 PM10/15/02
to mosul...@crtinc.com, perl6-l...@perl.org
> From: "mosul...@crtinc.com" <mosul...@crtinc.com>
> Date: Tue, 15 Oct 2002 14:33:28 -0400

I like the idea of this. The finer details, like returning what to
do, could be more elegant. But the extensibility idea is golden.

> To change how certain exceptions behave, a block simply changes the methods
> of the existing ExceptionCreator to point to other subroutines. This
> approach allows for an ala carte style of exception configuration. Blocks
> can (through a module that makes this sort of thing easy), clone the
> ExceptionCreator object, then change just the methods that are desired. A
> reference to that new object is passed down the line to child blocks.

I think a clone should be implicit (and lazy). Exception handlers
should be lexically (or dynamically?) scoped. Dynamically could be
useful in that if a module generated some known exception, you could
tell it what to do---on it's level. I worry that this could break
things too easily, though.

I definitely like this idea in that it eliminates a lot of
redundancy. Instead of 10 CATCH blocks doing the same thing with
different names, you just have one handler that does it all.
Exceptions are always something that seemed tedious to me (though I
have had some fun with them), and this might possibly relieve that.

Let's see if we can come up with a more elegant method than the return
constant thing.

Luke

mosul...@crtinc.com

unread,
Oct 15, 2002, 8:52:44 PM10/15/02
to perl6-l...@perl.org
From: Luke Palmer fibo...@babylonia.flatirons.org

> I like the idea of this. The finer details, like returning
> what to do, could be more elegant. But the extensibility
> idea is golden.

Thanks Luke. Your email made me think of another way of explaining the
concept. Basically, what I'm suggesting is that certain common types of
exceptions could be handled in-place, instead of having to write exception
handlers. Perl5 already does some things like this, e.g. undefs become
empty strings and zeros when needed. I'm just suggesting formalizing the
process more so that custom in-place exception handlers can be implemented.

Does that make the "return what to do" concept a little more elegant? If
not, feel free to work a Pygmalion with my Eliza Doolittle.

Dan Sugalski

unread,
Oct 16, 2002, 1:30:37 AM10/16/02
to perl6-l...@perl.org
At 2:33 PM -0400 10/15/02, mosul...@crtinc.com wrote:
>
>The constructor returns one of three values: an exception object, the
>DO_OVER constant, or the EXPRESSION constant.
>
>If an exception object is returned, that means that the interpretor should
>immediately exit the block, throwing the exception to that block's CATCH
>block, or its parent's CATCH block, and so on outward until somebody
>catches it. You know the routine.
>
>DO_OVER means to reevaluate the expression because the reference to the
>offending variable has been set to something else.

While a nifty idea, this is not going to happen, at least not in the
general case. Restartable exceptions are very, very difficult to do.
(And parrot, alas, isn't going to be able to do them) You still need
to establish an exception handler at the spot you want to restart at
(which isn't free, alas) and you run into problems when you cross
some internal boundaries in the interpreter.

That won't stop you from throwing a continuation when you throw your
own exceptions, which your own exception handlers can invoke when
they see fit, but low-level exceptions like division by zero and
suchlike things won't be able to do that.
--
Dan

--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk

Piers Cawley

unread,
Oct 22, 2002, 5:19:25 AM10/22/02
to Luke Palmer, mosul...@crtinc.com, perl6-l...@perl.org
Luke Palmer <fibo...@babylonia.flatirons.org> writes:

If you have 10 different exceptions that you want to handle in the
same way then you should probably look at subclassing them. Then all
you have to do is

try {
...
CATCH MyExceptionParent { ... }
}

--
Piers

"It is a truth universally acknowledged that a language in
possession of a rich syntax must be in need of a rewrite."
-- Jane Austen?

0 new messages