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

i'm using some tools built by someone else in my group, and these tools are littered with stuff like this:
eval "use Foo::Bar::Baz;"; die if $@;
the only reason i can see for these: the module being included isn't used in any other place but that sub.

this strikes me as a _bad_ idea, but it's more of an instinctual feeling than anything else.

i know that calls to 'eval' can be expensive, but is this a good way to use them?

i've used the block form of eval frequently, but this doesn't really look like an error that needed to be wrapped in an eval. i know that the memory footprint of the module in question would be a bit larger by adding the 'use Foo::Bar::Baz;' at the beginning of the module ( the more traditional way of 'use'ing modules ), but is that, in itself, a valid justification for this kind of runtime 'use' statement. ..

Replies are listed 'Best First'.
Re: runtime "use" statements via string eval
by footpad (Abbot) on Apr 04, 2001 at 04:52 UTC

    Well, I can't say whether or not it's completely ideal, but I've started using something like this to ensure that I've got all the modules I need:

    #!/usr/bin/perl -wT use strict; $|++; eval( "use MIME::Lite;" ); die "Module Configuration Error: $@" if $@; # and so on.

    Since I'm writing scripts for servers that I don't have have access to, I'm forced to test them on my personal resources, get them working, and then toss them into the Black Hole known as "Security Testing" in the company I work for.

    Personally, I like being able to tell the "experts" that they need to "tail their error log" in order to tell me what what wrong. *grin*

    A recent check via CB seemed to think this was an okay approach. As always, YMMV...

    Update: In reply to geektron's reply below:

    Ah, I get you. Yeah, that feels weird. To my mind, you should localize your uses and your uses/requires into a configuration section before doing any work. If it's gonna fail, get it to do so before you waste any time...unless you have a problem with people, like, y'know, deleting modules from underneath you. (And if that's going on, well, friend, you've got more issues than concurrency ones, if you know what I mean.)

    I spent about an hour poring over various perldoc entries and the closest I can find to anything that's documented is from perlmod,

    "You can switch into a package in more than one place; it merely influences which symbol table is used by the compiler for the rest of that block."

    Not entirely related, but if you read it obliquely, I think it suggests that there's no real benefit to using the module multiple times...unless (for some strange reason) you're modifying values in the module itself. Even then, I can't see how this would possibly make any difference. If you've used the module once, you've got to have a little bit of faith that it still exists within a few clock cycles.

    Thoughts? Feedback?

    --f

    Update #2: Fixed a rather nasty typo that damian1301 caught. (Thanks.)

      I've come to similar solutions as footpad with slight variations.

      In the first case a required module isn't installed and the script dies with a link to the dist on CPAN:

      eval("use XML::Simple 'XMLin'"); # required xml parser if($@){&install_xml_simple(); exit} sub install_xml_simple { print header; print qq~Install <a href='http://search.cpan.org/search?dist=XML-Simple'>XML::Simple</a +>~; }
      Second case just causes some form buttons and text to not be printed later:
      my$trade = 0; eval("use Compress::Zlib"); unless($@){$trade = 1} # if installed enable export/import
      And another switch if the script doesn't find a self-created data file:
      use vars qw($xpdat1 $dat1); my($xpdata,$data,$md,$nodat) = 0; # vars for next block my$df = 'some.data'; if(-e $df){ if(eval "require '$df'"){ $md = (stat($df))[9]; $xpdata = $xpdat1; $data = $dat1; } } else{ $nodat = 1 }
      there's one major difference, and i guess it wasn't quite clear from the first post.

      you're still useing the modules at the beginning, even though they're wrapped in an eval. and they're wrapped in an eval for a reason -- you're handing the scripts off to folks who may or may not have that resource.

      the issue, with more context:

      sub do_stuff { ### some stuff eval "use Foo::Bar::Baz;"; die if $@; ## do stuff with the imported functions }
      in some cases, the same  eval "use Foo::Bar::Baz;"; will end up in a different sub, same module. . .

      there's no 'hand off to client' - these are all internally-developed modules. . .

      i know that it's perfectly legal perl syntax . . . . it just doesn't sit quite right with me . .

      UPDATE: the thing is, and i haven't made it clear, is that the module being used in the eval string is NOT being used on 'initialization' ( for lack of a better word ). there's no 'use Foo:Bar::Baz;' at the beginning for the module. . .

      and i've been looking to see if there were any other issues than 'bad style'.

        First of all I think that external dependencies like that should be up front and obvious.

        Secondly each string eval ties up memory. If this is called in a tight loop, you will have a serious memory leak.

        A better way to do it is:

        require Foo::Bar::Baz; Foo::Bar::Baz->import();
        and leave out the second line if it is not necessary.
Re: runtime "use" statements via string eval
by chromatic (Archbishop) on Apr 04, 2001 at 06:48 UTC
    In this case, you're trading compile-time compilation (there's a good catchphrase for people looking to quote me) for run-time compilation on every pass through the subroutine.

    If it weren't for the string eval, I'd definitively say that you're not gaining anything on memory use -- if the subroutine is ever called -- as the eval() takes on the package context of the surrounding code. Any exported variables or methods will be installed in the current package table, as far as I understand it.

    Granted, if the module has already been used, Perl will look in %INC to see if it's there. You'll still have to pay the (however slight) eval string penalty.

    Besides all that, it's *generally* unclear and bad style, in my opinion. Unless there's a very compelling reason to do this, I'd whack somebody who suggested it. In general, there aren't many good reasons to use eval string.

      Seconded with the proviso that "aren't many" is not the same as "aren't any".

      A quick counter-example is a subroutine that takes a number of options and returns a closure with a large number of optional hooks. In this case it can be very nice to have the closure be the result of an eval, with the text of the subroutine being assembled from just the hooks you use. In other words move the cost of the hooks to tests when you compile the closure, and not pay every time you call the closure. (As I pointed out to the author, if Pod::Parser had a slightly different interface it would be an excellent candidate for getting a massive performance boost from this trick.)