My Favorite Modules: if

My blog post My Favorite Warnings: redundant and missing touched on the use of the if module. Comments on that post made me think it deserved a top-level treatment, expanding on (though not necessarily improving on) Aristotle's comment.

The if module is a way to conditionally load another module and call its import() or unimport() method. Sample usages are

use if CONDITION, MODULE, ...; # load and import
no if CONDITION, MODULE, ...;  # load and unimport

In actual use the CONDITION is an expression that is evaluated at compile time. If that expression is true the named module will be loaded and its import() or unimport() method called with whatever arguments were specified. If the condition is false, nothing is done. If MODULE is a string literal it will have to be quoted, since as far as Perl is concerned it is just another argument.

The usage in the blog that led to this post was

    no if "$]" >= 5.022, qw{ warnings redundant missing };

What this does is the equivalent of no warnings qw{ redundant missing }; if compiled on at least Perl 5.22, or nothing at all if compiled on an earlier Perl.

Note that the if module has nothing to do with the flow-control statement that is spelled the same way, even though use if ...; looks very like a suffix if.

I would also like to re-emphasize that everything is evaluated at compile time. Something like

my $condition = some_computation();
my $module = 'Some::Module';
use if $condition, $module;

will not do anything useful, because at compile time the variables are both undef.

use if some_computation(), 'Some::Module';

will work, but only if the subroutine exists when the use if ... is compiled.

4 Comments

Of course you can always make it work by sprinkling the requisite amount of BEGIN blocks all over the place, but pretty it ain’t:

my ( $condition, $module );
BEGIN {
  $condition = some_computation();
  $module = 'Some::Module';
}
use if $condition, $module;

If the some_computation sub is not defined at that time then you have to shuffle more code around to get it to be compiled earlier. It can always be made to work, it just gets uglier and uglier.

You can even eschew if.pm altogether:

BEGIN {
  warnings->unimport(qw( redundant missing ))
    if "$]" >= 5.022;
}

That particular example is relying on the fact that if you are turning off some warning categories then warnings must have been turned on before so warnings.pm is necessarily already loaded and a require warnings is therefore not needed. In the general case the code would be uglier still.

In summary, if.pm is neat.

Keep in mind that you can't avoid calling import on the module when it is loaded by if.pm.

use POSIX (); # doesn't call import
use if 1, 'POSIX', (); # does call import

Leave a comment

About Tom Wyant

user-pic I blog about Perl.