Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

How relevant is the order of 'use's ?

by Krambambuli (Curate)
on Oct 20, 2016 at 11:47 UTC ( [id://1174355]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks,

I have two simple modules:
package Demo1; use base qw/Exporter/; $SUCCESS = 1; BEGIN { use Exporter(); @ISA = qw(Exporter); @EXPORT = qw( $SUCCESS ) } 1;
and
package Demo2; sub import { ${[caller]->[0].'::'}{$_} = ${__PACKAGE__."::"}{$_} foreach grep { not /^(ISA|isa|BEGIN|import|Dumper)$/ } keys %{__PACKAGE__."::"}; } use constant { SUCCESS => 0, }; 1;
and a minimalistic test program, that is
#!/usr/bin/perl use strict; use warnings; use Demo1; use Demo2; print "SUCCESS: ", SUCCESS, "\n"; print "\$SUCCESS: $SUCCESS\n"; exit;
If I run the program as shown, I see an compile time error, like
Bareword "SUCCESS" not allowed while "strict subs" in use at ./demo.pl + line 9. Execution of ./demo.pl aborted due to compilation errors.
but if I simply change the order of the use instructions, i.e. I run
#!/usr/bin/perl use strict; use warnings; use Demo2; use Demo1; print "SUCCESS: ", SUCCESS, "\n"; print "\$SUCCESS: $SUCCESS\n"; exit;
then the displayed result is the expected one,
SUCCESS: 0 $SUCCESS: 1
I'd love to understand what's happening here - and would bve grateful to learn if there is a way to not have to use the two modules in a strict order in order to have the code working nevertheless.

Many thanks in advance.

Replies are listed 'Best First'.
Re: How relevant is the order of 'use's ?
by Eily (Monsignor) on Oct 20, 2016 at 12:55 UTC

    It seems to come from the way constant works.

    use Data::Dump qw/pp/; use constant { C1 => 10 }; sub C2 { 20 } say pp $::{C1}; say pp $::{C2}; __DATA__ \10 *main::C2
    So constants aren't actually just functions that are added to the symbols table, because instead of a glob there is a reference to the value.

    The problem with your code is that your import function replaces the whole entry in the symbols table (everything in $::{SUCCESS} is replaced by $Demo2::{SUCCESS}), instead of just the CODE section of the glob. Somehow it seems that constant makes the reference to the constant's value and the glob coexist, but when you remove the glob manually from the symbols table, perl can't find the symbol SUCCESS anymore.

    Funny thing is, if you just remove the line that prints the value of the constant SUCCESS, $SUCCESS gets printed, but with the constant's value. So with : use constant { SUCCESS => 5 }; in Demo2, the line print "\$SUCCESS: $SUCCESS\n"; prints "SUCCESS: 5".

    Something like $main::{SUCCESS} = $Demo2::{SUCCESS}->*{CODE} (under use feature 'postderef') may work better. (Edit: no it doesn't, since $Demo2::{SUCCESS} is a ref to a scalar, not a glob...)

      That's not exactly what constant does, it does this:

      use Data::Dump qw/pp/; use constant { C1 => 10 }; sub C2 () { 20 } say pp $::{C1}; say pp $::{C2}; __END__ \10 \20

      The empty prototype allows for inlining. See Constant Functions.

        Oh right, I forgot the prototype! Thanks :).

      yesss.... Your response, as well the previous one, gave the right direction.

      The defined import function is *not* OK. Replacing it with something closer to what Exporter does seems to solve the problem:
      package Demo2; sub import { #${[caller]->[0].'::'}{$_} = ${__PACKAGE__."::"}{$_} *{[caller]->[0].'::'.$_} = \&{__PACKAGE__."::$_"} foreach grep { not /^(ISA|isa|BEGIN|import|Dumper)$/ } keys %{__PACKAGE__."::"}; } use constant { SUCCESS => 0, }; 1;
      Now I just have to understand how come that it works :)

        Actually I think you can do even simpler/easier to read:

        use Exporter; use base qw/Exporter/; our %constants; BEGIN { push @ISA, 'EXPORTER'; %constants = (SUCCESS => 1); @EXPORT = map "&$_", keys %constants; } use constant \%constants;
        Since I guess your goal is to export all the constants in Demo2 without having to provide the list of names twice. And with the & appended to each name, there's no risk of overwriting $SUCCESS;

Re: How relevant is the order of 'use's ?
by stevieb (Canon) on Oct 20, 2016 at 12:59 UTC

    I haven't narrowed it down to a specific yet, but it appears as though your import routine is clobbering the $SUCCESS variable (perhaps confusing it with the constant with the same name), which is why things work if you use the packages in the opposite order (if Demo1 is loaded second, it initializes this variable *after* the clobbering in Demo2 has been done). You can see this for yourself by renaming $SUCCESS to something else. Also note that the use constant; line will be compiled/executed before the import sub is defined, which may have something to do with it.

    Not all packages have to be loaded in specific order, but some do. For example, I'm working on a project right now that uses numerous packages (and BEGIN blocks). One package needs to initialize a piece of external hardware as soon as it is used, but before any of its functions are called, and then another module does some work with that hardware when it is used. If the first module isn't loaded, the second one will break.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1174355]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (3)
As of 2024-04-20 01:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found