Search code examples
perlperl-module

perl use vs require and import, constants are only functions


I've got a Perl script that I'm trying to make compatible with two different Perl environments. To work around the two different versions of Socket I have, I'm doing a little hackery with require and import. I've got it working, but I'm not happy with the behavior.

Mod.pm:

package Mod;
use base 'Exporter';
our @EXPORT = qw( MAGIC_CONST );
sub MAGIC_CONST() { 42; }

test.pl:

use Mod;
#require Mod;
#import Mod;

printf "MAGIC_CONST = ". MAGIC_CONST ."\n";
printf "MAGIC_CONST = ". MAGIC_CONST() ."\n";

Outputs:

MAGIC_CONST = 42
MAGIC_CONST = 42

But using the 'require' and 'import' instead, I get this:

Output:

MAGIC_CONST = MAGIC_CONST
MAGIC_CONST = 42

So the question is: Is there a clean way I can get the normal behavior of the constants? I can certainly do sub MAGIC_CONST { Mod::MAGIC_CONST(); } but that's pretty ugly.

What I'm actually doing is something like this:

use Socket;
if ($Socket::VERSION > 1.96) {
  import Socket qw(SO_KEEPALIVE); # among others
  setsockopt($s, SOL_SOCKET, SO_KEEPALIVE); # among others
}

Solution

  • The reason the require version prints MAGIC_CONST instead of 42 is because use is what tells perl to import the symbols from one module to another. Without the use, there is no function called MAGIC_CONST defined, so perl interprets it as a string instead. You should use strict to disable the automatic conversion of barewords like that into strings.

    #!/usr/bin/env perl
    no strict;
    # forgot to define constant MAGIC_CONST...
    print 'Not strict:' . MAGIC_CONST . "\n";
    

    produces

    Not strict:MAGIC_CONST

    But

    #!/usr/bin/env perl
    use strict;
    # forgot to define constant MAGIC_CONST...
    print 'Strict:' . MAGIC_CONST . "\n";
    

    Produces an error:

    Bareword "MAGIC_CONST" not allowed while "strict subs" in use at ./test.pl line 4. Execution of ./test.pl aborted due to compilation errors.

    So if you want to use one module's functions in another, you either have to import them with use, or call them with the full package name:

    package Foo;
    sub MAGIC_CONST { 42 };
    
    package Bar;
    print 'Foo from Bar: ' . Foo::MAGIC_CONST . "\n";
    

    Foo from Bar: 42

    It's usually best to avoid conditionally importing things. You could resolve your problem as follows:

    use Socket;
    if ($Socket::VERSION > 1.96) {
      setsockopt($s, SOL_SOCKET, Socket::SO_KEEPALIVE);
    }
    

    If you truly want to import, you still need to do it at compile-time.

    use Socket;
    use constant qw( );
    BEGIN {
      if ($Socket::VERSION > 1.96) {
         Socket->import(qw( SO_KEEPALIVE ));
      } else {
         constant->import({ SO_KEEPALIVE => undef });
      }
    }
    
    setsockopt($s, SOL_SOCKET, SO_KEEPALIVE) if defined(SO_KEEPALIVE);