Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options

RFC: Hole In The Middle Pattern (Was Data Driven Programming)

by bennymack (Pilgrim)
on Aug 05, 2007 at 03:52 UTC ( #630688=perlmeditation: print w/replies, xml ) Need Help??

Dear Monks,

I'd like to do a quick follow up on Data driven programming? Not sure. I've come up with a much simpler, much more naive implementation that requires much less code to implement but still gives approximately the same affect. This new implementation does zero string evals, and no longer uses attributes.

The new implementation is called "ReRequire" and simply deletes a given module name from %INC and re-requires it (hence the name). This essentially means that modules that are written to be re-requirable are code compartments that allow the re-definition of constants at compile and run time.

Here is a quick example of Package::ReRequire in use.


package Package::ToBeReRequired1; use strict; use Carp(); use Package::ReRequire( CLUCK => 0, METHOD => 0, CONDITION => 0, THING => 'default_thing', ); sub get_sub { use warnings; return sub { my( $self ) = @_ if METHOD; my( $output ) = ( METHOD ) ? $self : ''; if( CONDITION ) { $output .= ' CONDITION '; }; $output .= ' UNCONDITIONAL '; $output .= THING; Carp::cluck __CALLER__ . ' clucking ' if CLUCK; return $output; }; } 1; __END__


package Package::ReRequire1; use strict; use warnings; # use Data::Dumper; $Data::Dumper::Terse=1; use Package::ReRequire(); sub import { Package::ReRequire::rerequire( 'Package::ToBeReRequired1', CLUCK => 0, METHOD => 0, CONDITION => 1, THING => __PACKAGE__, ); *function = Package::ToBeReRequired1::get_sub(); } 1; __END__


package Package::ReRequire2; use strict; use warnings; # use Data::Dumper; $Data::Dumper::Terse=1; use Package qw[ReRequire]; sub import { ReRequire::rerequire( 'Package::ToBeReRequired1', CLUCK => 1, METHOD => 1, CONDITION => 0, THING => __PACKAGE__, ); *method = Package::ToBeReRequired1::get_sub(); } 1; __END__

Then, you can do something like this to see what this is turned into:

$ perl -Mwarnings -e ' use B::Deparse; use Package::ReRequire1; print +f( "%s = %s\n", $_, B::Deparse->new->coderef2text( \&{ "Package::ReRe +quire1::" . $_ } ) ) for keys %{ "Package::ReRequire1::" };' function = { package Package::ToBeReRequired1; BEGIN {${^WARNING_BITS} = "UUUUUUUUUUUU"} use strict 'refs'; '???'; my($output) = ''; do { $output .= ' CONDITION ' }; $output .= ' UNCONDITIONAL '; $output .= 'Package::ReRequire1'; '???'; return $output; } BEGIN = ; import = { package Package::ReRequire1; BEGIN {${^WARNING_BITS} = "UUUUUUUUUUUU"} use strict 'refs'; Package::ReRequire::rerequire('Package::ToBeReRequired1', 'CLUCK', + 0, 'METHOD', 0, 'CONDITION', 1, 'THING', 'Package::ReRequire1'); *function = Package::ToBeReRequired1::get_sub(); } $ perl -Mwarnings -e ' use B::Deparse; use Package::ReRequire2; print +f( "%s = %s\n", $_, B::Deparse->new->coderef2text( \&{ "Package::ReRe +quire2::" . $_ } ) ) for keys %{ "Package::ReRequire2::" };' BEGIN = ; method = { package Package::ToBeReRequired1; BEGIN {${^WARNING_BITS} = "UUUUUUUUUUUU"} use strict 'refs'; my($self) = @_; my($output) = $self; '???'; $output .= ' UNCONDITIONAL '; $output .= 'Package::ReRequire2'; Carp::cluck('Package::ReRequire2 clucking '); return $output; } import = { package Package::ReRequire2; BEGIN {${^WARNING_BITS} = "UUUUUUUUUUUU"} use strict 'refs'; ReRequire::rerequire('Package::ToBeReRequired1', 'CLUCK', 1, 'METH +OD', 1, 'CONDITION', 0, 'THING', 'Package::ReRequire2'); *method = Package::ToBeReRequired1::get_sub(); }

To download the code and see the full test suite, check out:

$ export +ot $ cvs co my_modules

Creative name, I know! Ok, that's all for now. Let me know what you think. The interface is certainly not finalized. It's just the first thing I settled on that looks usable. All suggestions will be considered. Patches welcome. Thanks!

Update: Added more examples.

Replies are listed 'Best First'.
Re: RFC: Hole In The Middle Pattern (Was Data Driven Programming)
by diotalevi (Canon) on Aug 06, 2007 at 15:35 UTC

    The compiled code is effectively a global variable that cannot be read and modifications to it invalidate perl's method cache.

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      I guess I don't understand your reply.

      The compiled code is effectively a global variable that cannot be read

      My thought was that the compiled code would be a normal sub living in the stash like any other normal sub. Of course, any normal sub is "global" in that it can be accessed from anywhere via it's package name if that's what you mean.

      modifications to it invalidate perl's method cache.

      While it's true that re-requiring a module probably wreaks havoc with Perl's method caching, in the examples I have the calling module get a reference to the compiled sub. My thought was that since the calling module would *not* be re-required but act more as a normal module, all caching should work as usual.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://630688]
Approved by GrandFather
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2017-03-30 17:39 GMT
Find Nodes?
    Voting Booth?
    Should Pluto Get Its Planethood Back?

    Results (362 votes). Check out past polls.