http://qs321.pair.com?node_id=11109879

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

Hello monks,
I have a module XYZ.
I would like to use it in my code $USE_XYZ is enabled. I tried to add:
begin { if($ENV{USE_XYZ} == 1) { use XYZ; } }
but it looks like it still being used, even though $USE_XYZ is undefined.
How can I acheive this behaviour?

Replies are listed 'Best First'.
Re: Using perl module if env is enabled
by tobyink (Canon) on Dec 09, 2019 at 15:20 UTC

    use happens at compile time, so before the if gets checked.

    Yes, I know you've put it in a BEGIN { ... } block, so the if also happens at compile time, but the use is happening at a compile-time within compile-time, if you see what I mean.

    One solution is to not use use and use require;import instead.

    BEGIN { if ($ENV{USE_XYZ} == 1) { require XYZ; XYZ->import(); } }

    Another option is to take advantage of the core module if (if.pm).

    use if $ENV{USE_XYZ}, "XYZ";

    In rare cases, if you're loading something that does weird funky stuff at compile time, the first option won't work well. The second option should generally be okay.

Re: Using perl module if env is enabled
by davido (Cardinal) on Dec 09, 2019 at 16:42 UTC

    Keep in mind that for non-trivial code an optional dependency can cause spaghetti-like code with conditionals peppered throughout the code base:

    if(exists $INC{'Example/Module.pm'}) {...}

    ...or...

    if (__PACKAGE__->can('example')) {...}

    ...or any of a number of other strategies for detecting if the optional module got loaded. This is a situation where it's usually better to encapsulate the optional dependency so that the calling code doesn't have to care whether it was available or not:

    package SomeThing; use Exporter; BEGIN { if (eval "require MyOptional; 1;") { MyOptional->import('foo'); } else { *foo = sub { # fallback code here. }; } } use parent 'Exporter'; our @EXPORT = qw(foo); 1; package main; use SomeThing; foo();

    Now everywhere you wanted foo() you can just call it and not worry about whether or not it got loaded at startup by an external dependency, or by a fallback sub that you created yourself. This is just one of many ways to do it. An encapsulating OO class might work better, or dependency injection into a consuming object could be better; it depends a lot on your use case. But the goal is to reduce the pervasiveness of conditional logic in your code to only the one place.


    Dave

Re: Using perl module if env is enabled
by Corion (Patriarch) on Dec 09, 2019 at 15:19 UTC

    See use. use Foo::Bar; is basically BEGIN { require Foo::Bar; }. If you want some conditional logic, you can put that into the BEGIN block as well:

    BEGIN { if( $ENV{USE_XYZ}) { require Foo::Bar; Foo::Bar->import(); } }

    The if module wraps that simple case already:

    use if $ENV{USE_XYZ}, 'Foo::Bar';
Re: Using perl module if env is enabled
by haukex (Archbishop) on Dec 09, 2019 at 15:24 UTC

    In my experience, conditionally loading a module (like Corion and tobyink already showed) isn't always the only thing going on, since later on in your code you might want to use functions from that module conditionally, based on whether it was loaded in the first place. Personally, I prefer to set a global variable, something like this:

    our $HAVE_XYZ; BEGIN { $HAVE_XYZ = !!$ENV{USE_XYZ} } use if $HAVE_XYZ, 'XYZ'; ... if ( $HAVE_XYZ ) { XYZ::foo(...) }

    Also, note that BEGIN is uppercase, not begin {...}.

      A constant is usually better than a variable:

      use constant HAVE_XYZ => !!$ENV{USE_XYZ}; use if HAVE_XYZ, 'XYZ'; ... if ( HAVE_XYZ ) { XYZ::foo(...) }

      At it means that the if (...) stuff will be optimized away entirely when it's false. Also it will protect against $HAVE_XYZ being accidentally changed half way through running the process. (Of course, sometimes you do wish to start using XYZ later on by changing the variable, but that's probably less common.)

Re: Using perl module if env is enabled (-MXYZ PERL5OPT=-MXYZ perlcritic -brutal)
by Anonymous Monk on Dec 10, 2019 at 04:25 UTC

    Heh perlcritic knows about this


    $ perlcritic -brutal program.pl
    Code is not tidy at line 1, column 1. See page 33 of PBP. (Severity: 1)
    Missing strict or warnings at line 1, column 1. The strict and warnings pragmas are important to avoid common pitfalls and deprecated/experimental functionality. Make sure each script or module contains "use strict; use warnings;" or a mod ule that does this for you. (Severity: 4)
    Module does not end with "1;" at line 1, column 1. Must end with a recognizable true value. (Severity: 4)
    Code not contained in explicit package at line 1, column 1. Violates encapsulation. (Severity: 4)
    No package-scoped "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)
    Code before strictures are enabled at line 1, column 1. See page 429 of PBP. (Severity: 5)
    Code before warnings are enabled at line 1, column 1. See page 431 of PBP. (Severity: 4)
    Conditional "use" statement at line 3, column 9. Use "require" to conditionally include a module. (Severity: 3)

    See perlrun for ways to environmentally load a module

    perl -MXYZ ./myprogram.pl export PERL5OPT=-MXYZ set PERL5OPT=-MXYZ ./myprogram.pl
Re: Using perl module if env is enabled
by Anonymous Monk on Dec 10, 2019 at 16:40 UTC
    use if $ENV{USE_XYZ}, 'XYZ';

    The if module has been core since Perl 5.6.2.