This and other RFCs are available on the web at
  http://dev.perl.org/rfc/

=head1 TITLE

Handlers and Pseudo-classes

=head1 VERSION

   Maintainer: Nathan Wiger <[EMAIL PROTECTED]>
   Date: 14 Aug 2000
   Version: 1
   Mailing List: [EMAIL PROTECTED]
   Number: 101
   Status: Developing

=head1 ABSTRACT

Currently, there is no way to have multiple methods or modules for
dealing with events without significant contortions:

   $data = $r1->get_data || $r2->stuff || $r3->func;
   $r1->do_stuff(@vals) or $r1->do_this(@vals);

These simple cases can actually be tolerated. However, there are many
more complex cases which cannot be handled at all in Perl. These include
opening files only in certain directories, having methods decline or
partially handle requests, and so on.

This RFC proposes the idea of a C<handler>, which is a special type of
class that is actually composed of one or more classes. Their operation
is very similar to Apache handlers: requests can be handled, declined,
or partially handled, without the top-level program having to know about
it.

=head1 DESCRIPTION

=head2 Overview

The concept of a C<handler> is actually not that complex. In the
simplest case, it can be thought of as a type of abstraction:

   sub open_it {
       my $file = shift;
       return open $file ||
           HTTP::open $file ||
               FTP::open $file;
   }

Then, in your script, you would simply say:

   $fileobject = open_it "< $filename";

This gives you several benefits:

   1. The internal handling of open_it can be changed
      without having to update all your programs

   2. Each operation can actually partially process
      the request, if appropriate

   2. Your program is easier to read and understand

>From a Perl standpoint, these handlers work just like normal functions
and classes: they have methods, properties, inheritance, and so forth.
The only difference is that these handlers do not live in an external
file, but rather are assembled internally by Perl.

=head2 Basic Syntax

First, the examples assume that the reader is somewhat familiar with RFC
14. If not, it is recommended that you give it a quick read at
http://dev.perl.org/rfc/14.pod

There are several competing syntaxes that I have for this proposal. I've
provided the one that I think is the best, but this is open to
discussion.

The proposed syntax is to use a pragmatic style:

   use handler 'http' => 'MyHTTP';
   use handler 'http' => 'LWP::UserAgent';

This would assemble a C<handler> called 'http' which could then be used
in functions in your program. This handler would be a pseudo-class that
inherits methods from C<MyHTTP> and C<LWP::UserAgent>, in that order.
So:

   $fo = open http "http://www.yahoo.com" or die;

would call C<http->open>, consistent with the current Perl
implementation. The only difference would be that C<http> now tries to
call the C<open()> method from C<MyHTTP> and C<LWP::UserAgent>. As such,
the above call would result in the following flow chart:

 $fo         http->open                            undef
  ^              |                                   ^
  |              |                                   |
  |  Does MyHTTP::open exist?                        |
  |        YES/     \NO                              |
  |          /       \                               |
  |      Try it     Does LWP::UserAgent::open exist? |
  |       / \        ^      YES/     \NO             |
  |    OK/   \UNDEF /         /       ----------------
  -------     ------       Try it                    |
  |                         /  \                     |
  |                      OK/    \UNDEF               |
  -------------------------      ---------------------

Some highlights:

   1. Each class's open() method is tried in turn.

   2. If undef is returned, the next one in sequence
      is tried.

   3. If 'OK' (simply meaning 1 or some other true
      value, like $fo) is returned, that is propagated
      out and returned by the top-level handler.

   4. All classes are tried until 'OK' is returned
      or the last one is reached.
      
This allows you to easily chain classes and methods together with a
couple key benefits over an inline C<||>:

   1. Each handler can partially handle the request,
      but still return undef, deferring to the next
      one in line.

   2. The handlers can be reordered internally at-will
      without the main C<open http> code having to be
      redone.

   3. Different class open() methods can use internal
      rules, such as "only open .com URLs", without
      you having to put checks for this all over the
      place in the top-level program.

Note that C<open()> is the name of the method called on each class
because that is the name of the method called on the C<http> handler.
If:

   http->bob(@stuff);

was called, then C<MyHTTP::bob> and C<LWP::UserAgent::bob> would be
attempted, in that order.

=head2 Automatic Handler Registration

When a class is imported, it should be able to automatically register as
a member of a certain C<handler>. For example, the above code would be
better written as:

   use MyHTTP;            # these register as 'http'
   use LWP::UserAgent;    # handlers automatically

   $fo = open http "http://www.yahoo.com";

This means that there needs to be some mechanism for a module to execute
the equivalent of a 'use handler' statement, but have it take affect in
the package C<main>.

=head2 Handler Deregistration

In addition to handlers being added, they need to be removed as well.
This is where C<no handler> comes in:

   no handler 'http' => 'MyHTTP';   # remove MyHTTP from list
   no handler 'http';               # remove http handler

The first example removes C<MyHTTP> from the list of classes used by the
C<http> handler. The second syntax removes the C<http> handler entirely,
meaning that this call:

   $fo = open http "http://www.yahoo.com";

will result in the familiar error:

   Can't locate object method "open" via package "http"

This should obey blocks as well (like C<strict>), allowing you to say:

   {
      # force LWP::UserAgent to be used
      no handler 'http' => 'MyHTTP';
      $fo = open http "http://www.yahoo.com"; 
   }
   $fo2 = open http "https://www.etrade.com";

=head1 IMPLEMENTATION

We'll get to this later. This RFC is probably going to be revised a lot.

=head1 REFERENCES

RFC 14: Modify open() to support FileObjects and Extensibility

RFC 8: The AUTOLOAD subroutine should be able to decline a request 

http://www.mail-archive.com/perl6-language-io@perl.org/msg00086.html


Reply via email to