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

Reply via email to