After feedback from the Amsterdam Perl Mongers and some other people,
I'm considering some additions to the "load" pragma that I developed
and put on CPAN a few years back.
So what does "load" do? Well, it basically (optionally) delays
loading of subroutines until they're actually called (useful inside
cronjobs). *Or* it loads all subroutines of a module at compile time
(useful in an Apache mod_perl environment). And all of that with
minimal changes to the source code.
So what changes would you need to make to the source code? Well,
basically there are 2 changes you need to do:
1. add "use load;" near the top of the file
2. put all subroutines that need to be loaded "on demand" after an
__END__ marker
What the "use load" does, is that it scans the source file from which
it is being called between the first __END__ and the second __END__
or __DATA__ or end of file. In a mod_perl environment it will
immediately "eval" the subroutines. If not in a mod_perl
environment, it will create a directory of file offsets / lengths of
the subroutines it finds, which can be used later by an AUTOLOAD
handler to read from the source file and "eval" the subroutine when
it is actually being called.
So what are the disadvantages to this approach? Well, the most
noticeable one is the lack of support of file lexicals. Since each
subroutine is potentially compiled seperately, it cannot see file
lexicals (since they can only be "seen" during compilation).
The second disadvantage to this approach is the fact that it in
mod_perl, it is an all or nothing approach: either all subroutines
are loaded on demand, or all are loaded at compile time. I want to
achieve a greater granularity. For any given module, only load these
X subroutines on the basic application server, load these Y
subroutines on the XML server, and load these Z subroutines on the
administrative servers. Where X, Y and Z may be supersets, subsets
or intersections.
I was therefore thinking about adding the following generic
functionality to "load.pm":
1. support for related subroutines, grouped in a block
By adding support for the simple source parser for lexical blocks, it
would become possible to "share" lexicals between subroutines. This
is in fact no different from what some of us are already doing:
{
my $foo;
sub foo { $foo }
sub bar { $foo + $foo }
}
The source parser of "load.pm" would see this as one entity: whenever
"foo" or "bar" would be called, the entire block would be evalled,
causing "$foo" to be accessible by both "foo" and "bar".
2. support for "roles"
The concept of a "role" would be the conceptual context in which code
is being compiled. Taking the about X, Y and Z example, a module
might contain code that should be accessible in all 3 contexts, but
also code that should only be accessible (and compiled) in the Z
context.
I am thinking of adding a "compile time" directive to the "load.pm"
source scanner that would indicate in which "role" or "roles" the
code should be visible. Something like:
#roles: admin,app
sub foo { ... }
#roles: admin
sub bar { ... }
sub baz { ... }
A line that starts with "#roles:" would indicate the roles in which
the following code should be visible. In the above example, the
subroutine "foo" would only be visible in the "app" and "admin" roles.
In a mod_perl environment, it would compile only those subroutines of
that role. Outside of a mod_perl environment, the AUTOLOAD handler
would refuse to load any subroutines of which the role doesn't match.
The actual role for which code should be compiled, is set with an
environment variable, e.g. $ENV{ROLE}. Whenever code is encountered
that is not supposed to be available for that ROLE, measures will be
taken so that that code is not compiled (and execution errors will
ensue if you still try to do that).
3. preventing typo's in roles
To prevent typo's in role specifications, the first line with #roles:
should contain all possible roles that any subroutine in this file
could live in. This would also serve as a visual cue as to which
roles are supported for the developer. So for the above X, Y and Z
example, we'd probably have a line with:
#roles: admin,app,xml
near the top of the file.
4. making other code conditional on role
A constant would be exported (e.g. by default ROLE) that would allow
you to actually make conditions on the role:
if ( ROLE eq 'app' ) { # optimised away if ROLE ne 'app'
print STDERR "compiled for 'app' role\n";
}
5. propagate strict and warnings
Currently, any "use strict" and "use warnings" are not propagated to
code actually being evalled. Within the constraints of the simple
source code parser, it will try to remember the last setting of
"use/no strict" and "use/no warnings" seen. Alternately, the
import() routine of load.pm will allow specification of pragma's to
be prefixed to each piece of code being evalled. Something like:
use load(
use => 'warnings',
use => 'strict',
);
Perhaps that should even be the default setting.
I'm looking into this to scratch the itch of a client. It was
suggested to me to take this to a little bigger audience to see
whether maybe such a beast already exists. Or if it doesn't, to find
out whether there would be any suggestions, remarks or other feedback
that could be of interest. ;-)
Liz