Typed Yields: Non fatal exceptions

Wouldn’t it be nice to have:

begin {
&nsbr;LoadTheDatabase("foo.bar");
} rescue (Exception e) {
print "Fatal exception happened: ", e
} on (Warning w) {
print "Database Warning: ", w
} on (Message m) {
print "Database Message: ", m
}

The rescue (Exception e) should be familiar with everyone — something failed, maybe the database file was corrupted very bad, and raised an exception and the rescue block will be executed.

But what if the database has a small error, or something is only a little bit out of place. You wouldn’t want to just ignore it, but warn about it. Usually one would implement a ‘Logger’ class to which a function can log certain events, but that is ugly and inconvenient.

Enter non fatal exceptions. Basically there would be two ways to raise an exception, fatal like we all know it, and non fatal. When the on block for a non fatal exception has been executed, control will be returned to the function in which the raise was called.

This is done in about the same ways as a lot of languages implement yield. But this time the handling code depends on the type of the yielded object.

As far as Kaja and I concern this will be a feature of Paradox.

Thanks go to Bram for the idea.

6 thoughts on “Typed Yields: Non fatal exceptions”

  1. Do I understand well that if such a non-fatal exception occurs the code in the begin block will keep executing and is just interrupted by the execution of the “on” block? Isn’t that a bit odd, jumping around in the code like that? Catching an exception in a block in natural, the execution in the try/begin block is aborted and continues in the catch, but here it doesn’t. Here’s it’s just interrupted by the execution of the on block and then continues.

  2. Yes, that is how it works. It may seem odd, but I think it is cleaner than using a ‘messages list’ or callback delegate or yield to do similar stuff.

  3. At first, this sounded well enough and good. But there is a problem with having a lot of warnings/messages being thrown in a block of code. Because if a program chooses to ignore those warnings (ie. not specifically define an “on” block that can catch it), it must make its way back through all the exception handlers currently on the stack before the exception can be caught at the top level and probably just disregarded. This is quite expensive for a simple warning that the user doesnt even care about enough to catch.

  4. Good point.

    I’ve got two possible solutions:

    – Register exception handlers in a look-up-table, then the stack hasn’t got to be searched.
    – Or, only allow exceptions to travel one frame down in the stack. This is the case with .net — you need to explicitly rethrow exceptions if you want them to go deeper into the stack.

  5. Well there’s two ways to implement exception handling that I can think of off the top of my head. Both are very similar. Neither is perfect.

    One method is similar to how I implemented those macros that allowed exceptions in C. This method required no dynamic memory whatsoever (except maybe allocating the exception itself), but it required a longjmp() for every thrown exception (including the implicit rethrows if not caught by a handler).

    The other method is almost the same thing but requires a dynamic list of all the caught exception types for any given “begin” block. And it can iterate through the list of begin blocks until it find a begin block that catches the exception and does only one longjmp() to the proper begin block. While this method can find the appropriate rescue/on handler faster than the other method, it requires more dynamic memory. However, this is not so much of a problem in a language that has a good GC as it would be in C and C++.

    And personally, I am not a fan of having to explicitly rethrow. Based on the way that I program, I think it is more of annoyance than actually helpful (same with Java’s “throws” keyword on methods).

  6. I partially take that back. The second method still requires more overhead, but it does not necesarily require dynamic memory because the extra overhead could be placed on the stack as it is done in the first method.

Leave a Reply

Your email address will not be published.