develooper Front page | perl.module-authors | Postings from October 2008

Loading subroutines ad-hoc: extension to load.pm

Thread Next
From:
Elizabeth Mattijsen
Date:
October 15, 2008 02:46
Subject:
Loading subroutines ad-hoc: extension to load.pm
Message ID:
p06240825c51a94835d3d@[80.101.230.234]
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

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About