The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Package::Transporter - Export, Import, and AUTOLOAD under an OO interface

SYNOPSIS

Code from the documentation can be found in individual Perl files in the examples directory.

        use Package::Transporter sub{eval shift}, sub {
                $_[0]->register_drain('::Flatened', 'FOR_ANY',
                        'IS_', TRUE => 1, FALSE => 0);
        };
        print STDOUT ((IS_TRUE == 1) ? 'True' : 'False'), "\n";

        package Synopsis;
        use Package::Transporter sub{eval shift}, sub {
                $_[0]->register_drain('::Enumerated', 'FOR_SELF',
                        'ATB_', qw(HELLO  WORLD));
                $_[0]->register_potential('::Export', 'FOR_BRANCH',
                        'hello_world');
                $_[0]->register_potential('::Hello_Anything',
                        'FOR_SELF', 'salut_');
        };
        
        print "ATB_WORLD: ", ATB_WORLD, "\n";
        sub hello_world { print "Hello World.\n"; }
        

        package Synopsis::Desc1;
        use Package::Transporter sub{eval shift};
        
        print STDOUT ((IS_TRUE == 1) ? 'True' : 'False'), "\n";
        hello_world(); # first register_potential - export on demand
        
        
        package sisponyS::Desc2;
        our @ISA = ('Synopsis');
        use Package::Transporter sub{eval shift};
        
        my $obj = bless( \(my $o = 0), 'sisponyS::Desc2');
        $obj->salut_monde(); # second register_potential

        exit(0);

To check your understanding of what is happening above, see the tests for definedness in the documentation of Package::Transporter::Generator::Hello_Anything.

WARNINGS

This is an experimental module. It hasn't been thoroughly tested, making the probability of fatal bugs quiet high. The sole goal of the release is to document the current development.

The documentation is not keeping up with the development speed and carries fragments from various stages of development. However, the files in the examples directory (as well as the tests) document the use of all generators, although without explanation.

This package allows you to facilitate different origins for subroutine definitions and code in general, which are then passed to eval(). Control over an origin - file system or data base - means control over your program. Keep that in mind to avoid unpleasant surprises.

DESCRIPTION

Package::Transporter provides a concept for automated loading and distribution of subroutines. Subroutines can be loaded from various origins and in various forms. For example as text from a SQL database or more conventionally as references from another package. If a subroutine is immediately implemented during distribution, that is called drain. If a subroutine is implemented on demand, that is called potential.

Automated drain is mostly practical to generate constant functions and to distribute them into packages. Example: 'sub ATB_PRICE() { 0 }' as a symbolic index to the first element of a list - but without giving the full definition manually. Automated potential provides a leaner alternative to export, because you don't have to manually optimize the use of your namespace. Example for manual optimization: 'use POSIX qw(O_EXCL);'

Visit Points

By default a missing subroutine is defined inside a package via an eval. The eval is passed around through the anonymous subroutine reference sub{eval shift}. This construct is called a visit point for the purpose of this documentation.

It is actually a fast solution, easy to understand, and with straightforward results, bearing only a few surprises. Direct symbol table manipulations can't achieve the same, so its futile to discuss whether they are faster or slower with regards to the overall effort.

Every package using Package::Transporter gets an individual AUTOLOAD subroutine. That appeared to be the most robust approach and allows for a 'use strict' without exceptions.

Subroutine Generators

Subroutines are brought into life by generators. The following generators are shipped with Transporter and come with example code. Partial names refer to the base name Package::Transporter::Generator. Some generators are experimental or serve testing purposes.

::After_END

Copies missing subroutines from the __END__ section of the same package.

::Anonymous

Minimal generator for wrapping of pre-defined anonymous subroutines.

::Argument_in_Name

Hardwire one argument into the name of the subroutine.

::Automatic_Require

Attempt an 'require MODULE' for a missing MODULE::subroutine.

::Export

On-demand (proxy) export

::Fatal

Partial bridge to Fatal.pm

::Get_Set_Methods

Automatically created accessors and mutators

::Hello_Anything

Full example from synopsis.

::Homonymous_Packages

Subroutines from the same package in a different file

::Homonymous_Tie

Subroutines from a DB file of the same name as the package

::Interactive

AUTOLOAD interactive (write missing subroutines as the running program encounters them)

::Pipe

AUTOLOAD via a Unix pipe

::Pipe

Delayed object construction through a helper object.

::Require_Customized

Include customized file content

::SQL_Table

Store your subroutines in a database

::Shell

Bridge to Shell.pm

::Simple_Stubs

Simple subroutines stubs

::Suggested_Use

Issue the right 'use MODULE' to get the missing subroutine

Drain

TBD

Potential

Package::Transporter makes AUTOLOAD a two-line subroutine, which forwards all requests into a rule-based dispatch. Thus allowing more than one feature to be based on AUTOLOAD. Actual generation of subroutines is done by generator classes.

Simple rules allow generators to be attached to requests for missing subroutines. This way generators can be kept generic. Wildcards for package names and subroutine names are supported. A pre-selection tree ensures that only relevant rules are considered.

The $_[0] from the synopsis is a package object, which gives access to a package via a visit point. The object is monadic on a per-package basis, meaning that in the package Synopsis it is always the same $pkg and the 'new' is a 'new only if no old exists'. Execution of arbitrary code is done like this:

        $pkg = Package::Transporter->new(sub{eval shift});
        $pkg->transport(\$code, $argument1, ...);

Although the above is a more complicated way to say "eval $code", the $pkg object can be passed around and is available in the generator. Actually two $pkg objects are avaialble there, the one of the defining package ($defining_pkg) and the one of the consuming package ($pkg). The $arguments to transport enable exchange of living data structures between packages without serialisation into the string $code.

Package::Transporter affects only packages which use it. Consumption requires individual activation with the sub-eval-shift like shown in the synopsis. This module complements Package::Transporter.

The implementation should be straightforward and involves at least five method calls to respond to an AUTOLOAD request. Some informal code might illustrate what happens. Each step uses the results of the former step; details are omitted here.

        AUTOLOAD(...)
                $pkg->autoload(...)
                        $RULES->lookup(...)
                                $rule->check(...)
                                $rule->check(...) # eventually
                                ...
                        $generator->run(...)
                $pkg->transport(...)
        goto &$autloaded_sub

Motivation and Background

In Perl5 subroutines are brought into existence on demand via AUTOLOAD. It's a monolithic subroutine and handles exactly one package. Although AUTOLOAD is simple to use for trivial cases, it immediately looses its simplicity when doing OO programming. AUTOLOAD is subject to be overridden by other AUTOLOAD routines as part of OO. As a consequence, one has to find the parent AUTOLOADs with SUPER:: or mro::next::method and write them all in a specific way. Simplicity has already gone by then. That it the whole point.

Package::Transporter can be seen as an AUTOLOAD object, which hides the complexity of AUTOLOAD in non-trivial cases.

Another problem is the drain of Exporter. Autoloaded subroutines spring into existence when exported. In general that is more problematic than it sounds. Consider these two concepts of AUTOLOAD:

#1 Unlimited potential, which is tapped only when the missing subroutine is called. Export is about neither, because it requires full names in advance.

#2 Auto-vivification is conflict-free. AUTOLOAD never creates a subroutine with the same name of an existing subroutine. But export does that.

Again, in the general case one would like to see the potential being exported, not the drain. Package::Transporter provides that.

Last but not least, the functions can, exists and defined are not aware of AUTOLOAD. That is a good thing, because it allows for differentiated views. But one would like to test for potential, too. Package::Transporter adds new functions, which are called potentially_can and potentially_defined. They report potential without vivification of the subroutine. There is no potentially_exists, because it is a subtle distinction from defined, which can't be made with the information in AUTOLOAD.

Consumption via Search Paths

The search path is a list of package names, which are searched for rules. Rules decide autonomously whether they actually apply to a missing subroutine or not. There is no need to set the search path explicitly, but the default one has to be explained.

By default the search path consists of the components of the package name. For the package A::B::C the name hierarchy is A::B::C, A::B::, A::, and ''. Any rule found on the way to the top is checked and if the check is successful, the search stops. You can modify the search path with the search proxy methods as described in Package::Transporter::Path_Partition.

For method lookup a full hierarchical search is started in each parent class. The last three lines from the synopsis (full version in ::Hello_Anything), should remind you that the method salut_monde springs into existence as a subroutine in a parent class, because it is defined there only.

Convenience Function

The method register_potential has a convenience mode, where it accepts three-argument rule short cuts. The first argument is a generator, the second is a wildcard and the third is the matching subroutine pattern.

Minimally the generator is the textual body of a subroutine, which should at least generate textual bodies of other subroutines. Alternatively you can give a reference to a subroutine, a generator object or the (partial) name of a generator class. ('::Hello_Anything' will be interpreted as 'Package::Transporter::Generator::Hello_Anything').

The wildcard is one of the following four symbols:

  • a single plus '+' means the rule applies to the package itself only, although the generated subroutines follow inheritance;

  • a double colon followed by an asterisk b<'FOR_BRANCH'> means the rule applies to package names deeper in the hierarchy, so that the rules defined in Synopis applies to Synopsis::Desc1;

  • a combination of the above symbols '+::*' means a combination of their functions; and

  • a single asterisk 'FOR_ANY' means the rule applies globally other packages, including itself;

The third argument can be either a single subroutine name, an array reference of full names, a partial name ending in underscore or a textual regular expression (not the result of qr//).

Subroutine Potential

Whether a package has potential for a subroutine can be tested with the utility functions potentially_(can|defined). They are automatically created upon use. The functions take the name of a subroutine (meaning a string) as an argument and return true or false. That makes their use consistent, but different from from their static counterparts 'can' and 'defined'.

        if(potentially_defined('turbo_mode')) ...
        if($obj->potentially_can('turbo_mode')) ...

potentially_can does not return a subroutine reference, because it is about potential and thus it does not do autovivification. Beside that, potentially_can is potentially expensive. Remember Perl supports multiple inheritance, so that a search for rules has to go through the tree of parent classes (linearised though) and through the name hierarchy of each class. A lot of each to ensure that the method name in question springs into existence in the right defining parent class.

There is no potentially_exists, because the rule system in Package::Transporter doesn't distinguish between defined and exists, because AUTOLOAD doesn't tell whether a prototype is requested or a full subroutine definition. It would be useful to automatically generate prototypes, which would solve the problem described in in the BUGS section below. Background: 'exists' considers even prototypes and names treated with undef (crazy, eh?), while 'defined' only considers (fully) defined subroutines. You probably want to use the combination of can and defined for most practical purposes.

PUBLIC INTERFACE REFERENCE

The following methods belong to the public interface of Package::Transporter.

register_potential

Trigger the convenience function as described above.

BUGS AND LIMITATIONS

Please report unknown bugs via the "CPAN Request Tracker", which is the default way of managing such reports. If that is too much of hassle, then just drop me a note to the e-mail address below.

http://rt.cpan.org/NoAuth/Bugs.html?Dist=Package-Transporter

This is an early public release. Also see the Todo file. The documentation can't keep up with the development speed at the moment.

Common Pitfalls

AUTOLOAD is only triggered by Perl5, if something looks like a subroutine call. Paranthesis help a lot with that decision. Counter-example: my $result = calc_5 7, 8 expects calc_5 to exists at least as a (manually declared) prototype. But calc_5(7, 8) is fine for AUTOLOAD.

The statement 'use Package::Transporter' is executed during compile time. Some things are not available then. Like the DATA file handle or lexical variables. Get the package object again at run time with Package::Transporter->new, see sql_table.pl and DATA_section.pl.

Matching subroutine names with '' means that you promise to generate anything. It's most likely not what you want, because if you find out you can't, then there is no way out other than indicating failure. Example:

        $_[0]->register_potential('Some_Generator_Class', 'FOR_SELF', '');

Perl before 5.10

Uncomment the line with 'use MRO::Compat' and comment the one with 'use mro' to make Package::Transporter work with older versions of Perl5.

Prototypes

See the generator 'SQL_Table' for an attempt to have prototypes with AUTOLOAD. Keep in mind AUTOLOAD ignores prototypes (for unknown reasons), so the example is more an egg in a chicken-and-egg dilemma.

Imagine the following situation, where subroutine A calls B and vice versa (so that you can't simply change the order of their definition):

        sub A($) { ... B(2, 8); ... }
        sub B($) { ... A(7); ... }

Perl can't verify the arguments for the call to B(2, 8), because B is not defined, yet. The solution are subroutine prototypes (forward declarations). AUTOLOAD does not indicate whether a prototype definition is sufficient, so you can only solve the above situation by manually supplying prototypes.

Constant Functions

Constant Functions are inlined at compile time, when AUTOLOAD isn't considered, yet. Therefore you can't define constant functions via AUTOLOAD at the moment (Perl 5.10/5.11). Maybe subs::auto works around the issue, but hasn't been tested, yet.

Subroutine vs. Method Call

Generators for subroutines are searched along the package name by shortening it at the :: separators successively from right to left. For method calls this search is done in each parent class (there might by many - Perl has multiple inheritance). But what is an ordinary subroutine call and what is a method call? AUTOLOAD doesn't tell how the call is meant. The decision is done with defined(blessed($_[0])). In addition, if $_[0] is the current package name, the request is also considered a method call (constructor). Both criteria provide a good guess only, but are the best compromise currently available.

CORE::defined and UNIVERSAL::can

Both are left unmodified, because any re-definition could not be limited in scope. Meaning that a re-definition would cause any package to be unconditionally affected by a 'use Package::Transporter'. Absolutely not desirable, even if it sounds convenient. Individual AUTOLOAD routines and can methods might appear as unnecessary effort on the first sight, but they're the proper way of doing it.

Beside that, in a modified 'defined' you won't easily get the name of the subroutine it was called with. Example: defined(&some_routine) won't lead to 'some_routine' directly.

AUTHOR

Winfried Trumper <pub+perl(a)wt.tuxomania.net>

Copyright (C) 2010 Winfried Trumper

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

Motivation

A couple of years ago I made array-based objects the default for my Perl5 code and started to use constant functions instead of strings. It was a little bit scary to notice that in effect my code would work out of the box surprisingly often. Meaning after I fixed any mistakes reported by the compiler. Remaining sources of systematic errors were the manual definition of constant functions and the manual maintenance of @EXPORT. Both are addressed through automation with the help of this module.

Another limitation is the incompatibility of modules using AUTOLOAD. The warning 'Subroutine AUTOLOAD redefined at ...' isn't fatal, but the effect is. This module addresses the problem by replacing import with visit points and by adding a dispatch to AUTOLOAD.

SEE ALSO

Package::Transporter complements Package::Transporter in various ways.

Relations

Some of the exporter modules create subroutines on the fly, most notably Sub::Exporter and Class::Exporter. The compatibility of 'AUTOLOAD' and 'can' is addressed by Class::AutoloadCAN.