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/ >.