Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to import a constant with "use strict", avoiding "Can't use bareword ... as an ARRAY ref"

I have a module in a file that exports a constant that is an array reference. I can use that constant inside its defining module, but I cannot use it after importing it. The error message says Can't use bareword ("AR") as an ARRAY ref while "strict refs" in use at mod.pl line 28..

Consider this demo code:

#!/usr/bin/perl
require 5.018_000;

use warnings;
use strict;

package Test;

use warnings;
use strict;

BEGIN {
    require Exporter;
    our $VERSION = 1.00;                # for version checking
    # Inherit from Exporter to export functions and variables
    our @ISA = qw(Exporter);
    our @EXPORT = qw();                 # exported by default
    our @EXPORT_OK = qw(AR);            # can be optionally exported
}

use constant AR => [1,2,3];

print AR->[1], "\n";
1;

package main;
Test->import(qw(AR));
print AR->[1], "\n";
#Can't use bareword ("AR") as an ARRAY ref while "strict refs" in use at mod.pl line 28.

How can I fix it?

like image 317
U. Windl Avatar asked Jan 02 '23 05:01

U. Windl


2 Answers

You need to execute the import before a reference to the constant is compiled.

You could use yet another BEGIN block to do that, but that means we have now two hacks. Rather than frankensteining both the module's user and the module itself, I suggest the following approach. It keeps the inlined package looking as much as a real module as possible.

The approach consists of the following:

  1. Place the entire module as-is in a BEGIN block at the start of the script.
  2. Replace the trailing 1; with $INC{"Foo/Bar.pm"} = 1; (for Foo::Bar).

That's it. This allows you to use the module as normal.

So if your module is the following:

package Test;

use strict;
use warnings;

use Exporter qw( import );

our $VERSION = 1.00;
our @EXPORT_OK = qw(AR);

use constant AR => [1,2,3];

1;

And if your script is the following:

#!/usr/bin/perl
use 5.018;
use warnings;

use Test qw( AR );

say AR->[1];

You can use the following:

#!/usr/bin/perl

BEGIN {
    package Test;

    use strict;
    use warnings;

    use Exporter qw( import );

    our $VERSION = 1.00;
    our @EXPORT_OK = qw(AR);

    use constant AR => [1,2,3];

    $INC{__PACKAGE__ .'.pm'} = 1;  # Tell Perl the module is already loaded.
}

use 5.018;
use warnings;

use Test qw( AR );

say AR->[1];

As you can see, I've made some cleanups. Specifically,

  • If you're going to requires 5.18, might as well enable the language features it provides. This was done by replacing required 5.018; with use 5.018;
    • We don't need to use use strict; explicitly because use 5.012; and higher enable strictures.
    • We can use say because use 5.010; enables it.
  • A Test is not an Exporter, so it shouldn't inherit from Exporter. For the last 15-20 years, Exporter has been providing a better interface than the one you used.
  • No need to create or initialize @EXPORT if you don't need it.
  • No need for a BEGIN block around the initialization of @ISA and @EXPORT_OK.
like image 183
ikegami Avatar answered Jan 05 '23 18:01

ikegami


The print AR->[1] statement is parsed during compile time but the constant AR isn't imported into the main namespace until runtime.

The fix is to make sure that AR gets imported into main at compile-time

BEGIN { Test->import( qw(AR) ) }

There are also run-time workarounds

print &AR->[1], "\n";
print AR()->[1], "\n";
like image 26
mob Avatar answered Jan 05 '23 16:01

mob