README for Bioperl examples/root 

This directory contains some sample scripts and modules that
illustrate the use of the Bio::Root::* modules. Currently, these
example scripts focus on how exception handling. 

Here are some short descriptions of the examples/root scripts:

Script            Description
--------------    ----------------------------------------
exceptions1.pl    How to throw and catch Error.pm objects
exceptions2.pl    How to throw Error.pm objects via Bio::Root::Root
exceptions3.pl    Illustrates inheritance between Error.pm types
exceptions4.pl    Shows what happens when Error.pm isn't installed

These demo scripts should be executed within the
examples/root directory of the Bioperl distribution.


Using Error.pm for Exception Handling
--------------------------------------

The Bio::Root::Root module interfaces with Graham Barr's Error.pm.
Error.pm provides a handy way to create, throw, and catch exceptions
as objects. Error.pm is quite convenient and easy to use and adds a
level of control for managing errors within your Perl code using
familiar object-oriented, try-catch-finally semantics. You can define
subclasses of Error.pm representing particular types of exceptions,
and you can define catch blocks to handle these types of exceptions.

This has distinct advantages over simply catching any and all errors
with an eval{} block, as is currently done in Bioperl. Strongly typed
exception objects make it easy to write appropriate handlers. It also
makes you code easier to understand because it's clear what type of
things can/did go wrong.

Throwing exceptions that are Error.pm-compliant is a little more work
than throwing them the usual Bioperl way. Here's an example:

Using Error.pm-compliant syntax:

  if( !$feat->isa("Bio::SeqFeatureI") ) {
      $self->throw(-class => 'Bio::Root::BadParameter',
                   -text  =>"$feat is not a SeqFeatureI and that's what we expect.",
                   -value => $feat);
  }

Not using Error.pm-compliant syntax:

  if( !$feat->isa("Bio::SeqFeatureI") ) {
      $self->throw("$feat is not a SeqFeatureI and that's what we expect.");
  }

The advantage of using the Error.pm-compliant syntax is that, even if
Error.pm isn't installed, the exception message that gets thrown will
contain the name of the class of the exception. This provides a more
informative description of what went wrong.

In the Error.pm-compliant case above, the exception string starts with:

    ------------- EXCEPTION: Bio::Root::BadParameter -------------

Compare this to the non-Error.pm-compliant exception string:

    -------------------- EXCEPTION --------------------

There are a variety of exception classes that are declared in
Bio::Root::Exception for common types of error conditions:

    Bio::Root::Exception
    Bio::Root::NotImplemented
    Bio::Root::IOException
    Bio::Root::FileOpenException
    Bio::Root::SystemException
    Bio::Root::BadParameter
    Bio::Root::OutOfRange
    Bio::Root::NoSuchThing

Feel free to use these, or subclass from them to derive more specific
classes of exceptions. For more information about these types of
exceptions, see perldoc Bio::Root::Exception.

Error.pm is available through CPAN and I encourage Bioperl users and
developers to install it and experiment with it. For your
convenience, Error.pm has been included in the examples/root/lib
directory. As always, for the latest version, check CPAN.


Bio::Root::Exception.pm
-----------------------

The Bio::Root::Exception.pm module contains a number of Error.pm
subclasses representing common types of errors. If you want to throw
an exception within your Bioperl module that doesn't correspond to any
of the ones defined in Bio::Root::Exception, feel free to define a new
one, but be sure it inherits from Bio::Root::Exception or one of its
subclasses. This will allow anyone to write a handler for any type of
Bioperl exception.

Defining a new type of exception can be done quite simply. All you
need to do is to specify the @ISA array for your new type, as in:

    @Bio::Root::Exception::MyBad::ISA = qw( Bio::Root::Exception );

If you want to override any of the available methods or add new ones,
you'll have to provide a package statement and the appropriate
method definitions.

Programming tip: Be careful not to use exceptions as your primary
means of flow control within your code. Throwing and handling
exceptions come with some execution overhead. Also, such excessive use
of exceptions can make your logic hard to follow.


Bio::Root::RootI.pm and Bio::Root::Root.pm 
-------------------------------------------

The modules in the lib directory also demonstrate the use of the Bioperl
modules Bio::Root::RootI and Bio::Root::Root. RootI.pm should be used
as the base class for any Bioperl module that specifies an
interface. It simplifies the process of writing virtual
methods. Root.pm implements RootI.pm should be used as a base class
for any Bioperl module that specifies a concrete object.

The module TestInterface.pm demonstrates how to use
Bio::Root::RootI.pm. The module TestObject.pm demonstrates how to use
Bio::Root::Root.pm.

Bio::Root::RootI defines a method called "throw_not_implemented()"
that will throw a Bio::Root::NotImplemented exception. This is useful
for ensuring that an implementing class has implemented all
methods. Any method within a Bio::Root::RootI subclass can call
throw_not_implemented() to indicate that a method has not been
implemented. Implementations of the interface must implement the
method or an exception will result when someone tries to use it.

Note that Bio::Root::Root can make use of Error.pm if available, but
Error.pm is not required. 


Bio::Root::Root::throw() with Error.pm
---------------------------------------

Bio::Root::Root can determine if Error.pm is available and if so, can
make use of it when Bio::Root::Root::throw() is called. For a demo,
see test2.pl.


Real-Life Examples
------------------

For additional examples of how to make use of the Error.pm-related capabilities
of Bio::Root::Root.pm, I created new versions of Bio::SeqI.pm,
Bio::Seq.pm, Bio::PrimarySeqI.pm, and Bio::PrimarySeq.pm within the
lib/Bio subdirectory. This conversion is pretty straightforward and could
be done on the other Bioperl modules without too much effort.

TODO: Update the lib/Bio modules based on the latest versions in bioperl-live.


Using Error.pm's try{} and catch{} within Bioperl Modules
----------------------------------------------------------

For developers, using Error.pm's try{} and catch{} blocks within
Bioperl modules themselves could come in handy. But doing so would add
an external dependency for Error.pm, which is not part of the standard
Perl distribution. So at this stage, it's best to stick with just
using Error.pm's throw() method (via Bio::Root::Root) and leave the
try{} and catch{} blocks for use only within your scripts.

If you really want to use try{} and catch{} within your module and
still want to be capable of running when Error.pm isn't available, you
can check $Bio::Root::Root::ERRORLOADED variable.

If we really want to incorporate it within Bioperl, a reasonable
solution would be to distribute Error.pm with Bioperl. 

So why use Error.pm instead of some other utility? Well, Perl 6 will
most likely include some form of structured exception handling akin to
that provided by Error.pm (see these RFC's:
http://dev.perl.org/rfc/63.pod and http://dev.perl.org/rfc/88.pod).
So it will probably be easy to convert Error.pm-based exception handling
to whatever is adopted for Perl 6.

(Side note for any CORBA folks out there: Error.pm is used in some
other CPAN modules, notably CORBA::MICO. Thus, using Error.pm within
Bioperl allows consistent exception handling methodology when working
with such modules and Bioperl together.)

--
Steve Chervitz <sac@bioperl.org>
21 April 2001
Updated 6 March 2003

