http://www.perlmonks.org?node_id=1083580

Theodore has asked for the wisdom of the Perl Monks concerning the following question:

I have seen in Use of %INC ? the answer probably never, and if you do, just for read access to the question when %INC should be used. But I have messed with %INC and I would like to know if there is a better way to do the same thing.

I wanted to be able to redefine a subroutine without restarting the main program process. The subroutine is in a separate .pl file and this specific file is changed: I want to redefine the subroutine with the new file contents without stopping and restarting the main process. In the case of compilation errors in mysub.pl, the main program should not die.

Here is some demo code:

The subroutine file (mysub.pl):

sub mysub { print "foo\n"; }; 1;
The main program:
#!/usr/bin/perl use strict; my $fn = './mysub.pl'; require $fn; while (<STDIN>) { chomp; my $in = $_; if ($in eq 'load') { delete $INC{ $fn }; my $code = "require '$fn'"; eval $code; if ($@) { print "ERROR! Keeping old code.\n"; } else { print "mysub replaced.\n"; }; }; &mysub(); }; exit 0;
Run the main program. Pressing enter, prints "foo". Now, without stopping the main program, change the print statement in mysub.pl to print "bar". Enter 'load' in the main program. After "mysub replaced", "bar" should be printed.

If you omit the delete $INC{ $fn }; line, the subroutine as expected is not redefined. Am I doing something wrong here? Is there a more "mainstream" way to achieve the same thing?

Update: The answer is here: Re: Messing with %INC.

Replies are listed 'Best First'.
Re: Messing with %INC.
by Anonymous Monk on Apr 24, 2014 at 13:07 UTC

    How about doing the file? (See also the description and pseudocode in require.)

      Indeed, using do should be lighter and 1 line of code shorter.
      my $code = "require '$fn'"; eval $code;
      can be replaced by:
      do $fn;
      Thanks!

        More than that: do re-evals the file each time it is called, so your manipulation of %INC is not necessary.

Re: Messing with %INC.
by mr_mischief (Monsignor) on Apr 24, 2014 at 13:36 UTC

    Well, this is certainly nicer than opening and reading a file full of code and then doing an eval on the code. Is there any way, though, that you could reread a configuration file that changes the behavior of the program and select among definitions of the routine?

    Is this specifically a system designed to handle code plugins that are maintained separately and reload those while running? What you have is a fairly clean and straightforward way to do that I think. If those other files are not separately managed, though, I'd say write all your routines out and choose them dynamically through a reference rather than redefining them this way.

      Is this specifically a system designed to handle code plugins that are maintained separately and reload those while running?
      It's exactly that.

        In that case, you may find mr_mischief's suggestion of using code references (such as anonymous subs in a hash) a much more flexible approach.

        For example, the different plugins can reside in their own packages, and register themselves with the main program, instead of having all plugins be in the same package or the main program having to figure out where the plugin's functions are.

        Reloading a file can be useful if you're reloading the same file repeatedly (e.g. after changes).

Re: Messing with %INC.
by Preceptor (Deacon) on Apr 24, 2014 at 14:23 UTC

    I think the reason they say 'don't mess with %INC' is more because of redefining subroutines on the fly. Why do you want to do this?

    It's a source of some potentially substantial pain, bugs and exploits. If I were really desperate to try something like this, I might be inclined to define anonymous subs - get some code, wrap in an eval, make an anonymous sub and pass it around by reference.

Re: Messing with %INC.
by Anonymous Monk on Apr 24, 2014 at 13:11 UTC
      Module::Reload reloads everything. It's actually a simpler way to do what Module::Reload::Selective does, which was my first option, but a bit complicated for the specific case.