Re: Exception handlers [was: Traps for module writers]
Peter Seibel (seibel@organic.com)
27 Nov 1996 09:26:39 -0800
"DL" == Daniel LaLiberte <liberte@ncsa.uiuc.edu> writes:
DL> Nick Ing-Simmons <nik@tiuk.ti.com> writes:
>> Don't need a keyword - we can use a prototype - wrote this for Tk
>> yesterday in light of this thread here :
>>
>>
>> # a wrapper on eval {} which turns off user $SIG{__DIE__}
>> sub catch (&)
>> {
>> my $sub = shift;
>> eval {local $SIG{'__DIE__'}; &$sub };
>> }
DL>
DL> A standard routine like this 'catch' will be useful, but I think the
DL> name should be changed. In the exceptions.pl file in the Perl
DL> library, 'catch' and 'throw' are defined to act like Lisp's catch
DL> and throw. A throw of a specific tag is caught by a currently
DL> active catch that says it will catch that tag, and control proceeds
DL> after that catch. A cleanly defined catch module would be handy.
DL>
DL> But another useful Lisp routine is 'unwind-protect' (I'm not
DL> crazy about the name). It calls some finalization code no matter
DL> what happens in some body code. A 'protect' call might look like:
DL>
DL> protect { some body code }
DL> finally { some finalization code }
DL>
DL> The 'catch' Nick defined is basically 'protect' without a 'finally'
DL> clause, I think.
DL>
DL> More generally still, we need something like Lisp's condition-case.
DL> Exceptions (which might be either names or objects) may be
DL> 'raise'd. Each currently active condition-case is checked until a
DL> handler for the exception is found. The exceptions that a specific
DL> handler will handle can be specific (e.g. arithmetic-error) or
DL> general (e.g. error).
DL>
DL> The condition-case idea can be combined with the unwind-protect and
DL> catch/throw ideas into one general exception handler. Tags that
DL> may be thrown would just be one kind of exception. The
DL> finalization code, if present, would be executed no matter what.
DL> So the 'protect' with handlers might look like:
DL>
DL> protect { some body code }
DL> when ( exception(s) to handle ) { handler code }
DL> when ( exception(s) to handle ) { handler code }
DL> finally { finalization code }
DL>
DL> I don't know what syntax Perl would allow, or how the
DL> exceptions-to-handle would be specified, but this is the basic
DL> idea. There are more complications regarding whether some
DL> exceptions are continuable (from the point the exception was
DL> raised), or whether full continuations are possible as an even more
DL> general mechanism for building exception handlers and other control
DL> constructs.
A while back I hacked this thing up, after going back and reading *all*
the discussion of exception handling I could find in the p5p archive. I
even read a chapter out of a Modula-3 book about how they do exceptions
(since one of the most passionate participants in an earlier discussion
was a real Modula-3 fan.) And I looked at how Common Lisp does
exceptions. And Java. So this is either the best of all possible worlds
or just an incoherent mishmash.
Anyway, this has, at least, both the features requested above: a finally
clause as well as typed exceptions. And through (in)judicious use of
prototypes I got the syntax fairly clean. In use it looks like this:
#!/usr/local/perl5.00308/bin/perl -w
use strict;
require 5.003;
package BazException;
use vars '@ISA';
@ISA = qw ( Exception );
package main;
use Exceptions;
## Common Lisp/Java style
try {
print "here in try clause\n";
# Try uncommenting different lines below to get different behavior
#die "Fooey";
#throw Exception;
throw BazException;
} finally {
print "Bazmobile\n";
} catch BazException with {
print "Caught BazException.\n";
print $E->dump, $/;
} otherwise {
print "Some other exception\n";
print $E->dump;
};
## Or if you're in a Modula-3 kind of mood.
try {
print "Here in try clause of Modula-3 style exceptions.\n";
raise Exception;
} finally {
print "In finally\n";
} except {
BazException => sub { print "Caught raised BazException.\n"; },
Exception => sub { print "Caught raised Exception.\n";
print $E->dump, $/; },
};
__END__
The finally clause runs *before* the catch (or except) clauses. I forget
now why; I think because that's how either Lisp or Java or both did
it. Or maybe because otherwise there's no good way to guarantee it gets
run. Anyway, I can post the code of the Exceptions module if folks are
interested.
-Peter
--
Peter Seibel Perl/Java/English Hacker seibel@organic.com
Clarity through obfuscation.
# Organic Online is hiring Perl5/CGI programmers.
# Email or see < http://www.organic.com/jobs/ >.