Search code examples
perlperl-module

Importing variable into Perl package


I'm writing a basic program whose core logic is split across several project-specific modules for cleanliness (keeping subroutines organised by their purpose in the program's logic).

Suddenly had trouble exposing an option from the main package in one of the modules, and using the our statement appeared to have no effect.

For brevity, I'll copy+paste an isolated test case I wrote to examine this behaviour:

main.pl

#!/usr/bin/perl
use warnings;
use strict;
use File::Basename;

# The variable to be read by the module.
our $verbose = 1;

# Load Output.pm from directory
use lib dirname "$0";
use Output;

write_message "Hello, world\n";

Output.pm

package Output;
use warnings;
use strict;
use parent "Exporter";
our @EXPORT = qw(write_message);

# Should be imported?
our $verbose;

sub write_message {
    print $_[0] unless !$verbose;
}

1;

Expected result: "Hello, world"
Actual result: Dead silence

It's quite possible that what I'm trying to achieve isn't even possible in Perl, as this isn't the intended use of modules (and heck, I understand why that'd be the case).

I'm still quite new to Perl and there are some things I'm struggling to wrap my head around. I've seen people recommend using the our declarator to expose a variable across packages, but I can't understand why this isn't working.

PS: If anybody knows a better approach to splitting an app's program-specific logic between modules, I'd appreciate some pointers too. :) But first and foremost, I'd prefer to learn why our-ing a variable isn't working.


Solution

  • An our statement just creates a package variable (whereas my creates a lexical variable). It has nothing to do with exporting

    The best option is probably to declare the variable in the Output package and access it as $Output::verbose elsewhere. Like this

    main.pl

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use File::Basename;
    use lib dirname $0;
    use Output;
    
    $Output::verbose = 1;
    
    write_message "Hello, world\n";
    

    Output.pm

    package Output;
    
    use strict;
    use warnings;
    
    use Exporter 5.57 'import';
    our @EXPORT = qw/ write_message /;
    
    our $verbose;
    
    sub write_message {
        print $_[0] if $verbose;
    }
    
    1;
    

    Note that I have also removed the incorrect quotes from around $0, and ever since version 5.57 of Exporter it has been possible (and preferable) to import it's import subroutine instead of subclassing it