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

The premise of 'use Foo;' is that there is a file 'Foo.pm' in which there is a definition of 'package Foo'. However, I want to have a set of files 'test_v1.pm' .. 'test_vN.pm' that each have definitions of 'package Foo' and 'package Foo::Bar'. That is, the module file names don't match the package definitions, and each module file defines multiple packages. I think I can use the '-norequire' tag to get the second package defined (right?), but I still don't understand how to import packages that don't have the same name as the module file. I've read perlobj and perlootut, but I still can't figure this out. A little help please?

Replies are listed 'Best First'.
Re: Multiple Packages in a Module?
by haukex (Archbishop) on Jun 25, 2018 at 16:31 UTC

    In Perl, there is no required/fixed relationship between the name of a file and which/how many packages it contains, just a "best practice" to put a package named e.g. Foo::Bar in a file Foo/Bar.pm, because many tools (and people) expect this. So I'd suggest sticking to that practice unless you've got a good reason (which you haven't explained yet).

    use Modulename ...; is equivalent to BEGIN { require Modulename; Modulename->import(...) }, so in the case you describe (Update: by which I mean, merging a package Foo in Foo.pm with package Foo::Bar in Foo/Bar.pm into one file test_v1.pm), one way to do the equivalent of use would be BEGIN { require test_v1; Foo->import(); Foo::Bar->import(); }. This will attempt to load a file test_v1.pm from one of the paths in @INC if it hasn't been loaded yet (see require), and it will call those two packages' import methods.

    However, by your mention of -norequire I'm guessing you're using the parent pragma, and OO can be a little trickier. It would be best if you could show a minimal code example, see Short, Self-Contained, Correct Example.

    Further reading: In this node I wrote about the basics of importing from modules (in a slightly different context, but still applicable here), and I recommend a thorough read of perlmod.

    Minor edits.

      haukex: ...the equivalent of use would be BEGIN { require test_v1; Foo->import(); Foo::Bar->import(); }.
      Actually, no. The equivalent would be BEGIN { require test_v1; test_v1->import; }.
        Actually, no. The equivalent would be BEGIN { require test_v1; test_v1->import; }.

        No, because I wrote "in the case you describe", which is that there is one file test_v1.pm with two packages, Foo and Foo::Bar (no mention of a package test_v1). The code I showed is the equivalent of splitting that one file into two (Foo.pm and Foo/Bar.pm) and useing them individually.

Re: Multiple Packages in a Module?
by haj (Vicar) on Jun 25, 2018 at 17:00 UTC

    A matching between the identifiers following package and use is not enforced by Perl, albeit it is a convention which is useful in most cases.

    • use Foo; instructs Perl to look in its list of module directories for a file called Foo.pm.
      • Edited to add (thanks tobyink): Then it calls Foo->import.
    • package Foo; defines the namespace for the following block (or file, if not in a block).
    • I know about the -norequire tag only in context of use parent 'Foo'. In that use case, 'Foo' is both a namespace for inheritance, and also instructs Perl to load Foo.pm behind the scenes. The -norequire suppresses this loading part.
    You can easily have many files containing package Foo; and, later in the same file, package Foo::Bar. In your example, you would then say use test_v1; and have all symbols in both packages available. You need to understand, however, that if you also use test_v2; in the same program and if this file also defines stuff in the Foo or Foo::Bar namespaces, then these definitions can overwrite each other (triggering warnings as they do). Here's an example of two different implementations of a Foo::Bar class:
    # File test_v1.pm use strict; use warnings; package Foo; sub new { my $class = shift; bless { version => 1 }, $class; } sub version { my $self = shift; return $self->{version}; } package Foo::Bar; use parent -norequire, 'Foo'; 1;
    # File test_v2.pm use strict; use warnings; package Foo; sub new { my $class = shift; bless { version => 2 }, $class; } sub version { my $self = shift; return $self->{version}; } package Foo::Bar; use parent -norequire, 'Foo'; 1;
    ...you can then use either of them:
    1. perl -Mtest_v1 -E "say Foo::Bar->new->version" # prints 1
    2. perl -Mtest_v2 -E "say Foo::Bar->new->version" # prints 2

      Anonymous Monk:   Note also that in a file containing multiple package namespaces, a block structure can be very useful to achieve complete lexical isolation within each package:
          package Foo { my $private; ...; ... }  # perl version 5.14+
          package Bar { my $private; ...; ... }
      or (note required semicolon after package statement)
          { package Foo;  my $private; ...; ... }  # pre-5.14 perl
          { package Bar;  my $private; ...; ... }
      (The pre-5.14 scoping will, of course, continue to work with version 5.14+.) And definitely make use of the life-simplifying parent module!

      Update: And in any version of Perl 5, this scoping can be used to implement absolutely private package/class functions/methods:

      package Foo { ... my @stuff = ( ... ); # initialized upon inclusion ... my $private_function = sub { ... }; ... my $private_class_method = sub { my $class = shift; ...; }; my $private_object_method = sub { my $obj_ref = shift; ...; }; ... $private_function->(@stuff); ... $class_name->$private_class_method(...); $object_reference->$private_object_method(...); ... }
      (This assumes that all these separate packages are contained in a standard .pm module that is use-ed or require-ed in the, well, usual way so that phasing problems are eliminated — or at least minimized.) The naming is a bit misleading in that both the  $private_class_method and  $private_object_method methods can be invoked with either a class name or an object reference, although what you do with the class name/object reference therein is another story! But they're still private.

      All this, it must be said, starts to sound like a real OO system, of which there are many thorough-going implementations in CPAN.


      Give a man a fish:  <%-{-{-{-<

      "use Foo; instructs Perl to look in its list of module directories for a file called Foo.pm."

      It does a bit more than that. It also runs Foo->import() after loading Foo.pm.

        haj: "use Foo; instructs Perl to look in its list of module directories for a file called Foo.pm."
        tobyink: It does a bit more than that. It also runs Foo->import() after loading Foo.pm.
        That's correct, and I am guilty for oversimplification with regard to that. I am sorry for not having written a more detailed description which could have also clarified a few other inaccuracies which have surfaced in this thread. Sigh. I made some assumptions about the intentions in the original post, but obviously other monks have made other assumptions.
Re: Multiple Packages in a Module? (manipulate %INC)
by LanX (Saint) on Jun 25, 2018 at 18:45 UTC
    > I want to have a set of files test_v1.pm .. test_vN.pm that each have definitions of package Foo and package Foo::Bar

    After useing test_v*.pm you have to make Perl believe that package Foo and package Foo::Bar have already been loaded ...

    i.e. you have to flag it in %INC: °

    • %INC

    The hash %INC contains entries for each filename included via the do, require, or use operators. The key is the filename you specified (with module names converted to pathnames), and the value is the location of the file found. The require operator uses this hash to determine whether a particular file has already been included.

    I had a similar discussion some time ago, I'll dig it up and update it here.

    EDIT

    Couldn't find it, here a little demo:

    use strict; use warnings; use Data::Dump qw/pp dd/; package Foo::Bar; sub import { warn __PACKAGE__." was imported!" } BEGIN { $INC{'Foo/Bar.pm'}=1; # actually a path needed, but 1 is true eno +ugh˛ ;-) } package main; use Foo::Bar; warn pp \%INC;
    Foo::Bar was imported! at d:/Users/lanx/pm/inc.pl line 10. { "C:/Perl_64/site/lib/sitecustomize.pl" => "C:/Perl_64/site/lib/sitec +ustomize.pl", "Data/Dump.pm" => "C:/Perl_64/lib/Data/Dump. +pm", "Exporter.pm" => "C:/Perl_64/lib/Exporter.p +m", "Foo/Bar.pm" => 1, "overload.pm" => "C:/Perl_64/lib/overload.p +m", "overloading.pm" => "C:/Perl_64/lib/overloadin +g.pm", "strict.pm" => "C:/Perl_64/lib/strict.pm" +, "subs.pm" => "C:/Perl_64/lib/subs.pm", "vars.pm" => "C:/Perl_64/lib/vars.pm", "warnings.pm" => "C:/Perl_64/lib/warnings.p +m", "warnings/register.pm" => "C:/Perl_64/lib/warnings/r +egister.pm", } at d:/Users/lanx/pm/inc.pl line 21.

    HTH! :)

    update

    added dump of \%INC for illustration

    update

    ˛) using __FILE__ instead of 1 is even better, because the origin of the code will become obvious when debugging.

    BEGIN { $INC{'Foo/Bar.pm'}=__FILE__; }

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    °) %INC the hash not @INC the array!

      LanX: After useing test_v1.pm you have to make Perl believe that package Foo and package Foo::Bar have already been loaded ...

      That makes only sense if you want to prevent Foo/Bar.pm being read from disk if a use Foo::Bar; is executed after the usurping module test_v1.pm has been loaded. Depending on load order can be tricky if Foo/Bar.pm initializes stuff or runs BEGIN blocks.

      Unfortunately we don't know the intentions of the original poster, so apparently the lot of us have made different assumptions. I have seen (and used) multiple packages in one module in these cases:

      • For "internal" classes which are not seen from the outside, where no one is ever supposed to "use" the module. In that case, modifying %INC isn't necessary.
      • To modify the behaviour of classes in a test: In that case, my test_v1.pm would explicitly use Foo::Bar and then do the desired modifications in a block { package Foo::Bar; no warnings "redefine"; ...; }. Modifying %INC isn't necessary either.

      Cheers,
      haj

        I have problems to see a case where my approach doesn't work.°

        A use is a require + import and the require part is skipped after checking %INC

        (Of course you need to place Foo::Bar before Foo if the latter uses the former.)

        But if you have a special example in mind where this breaks, please share.

        update

        > if a use Foo::Bar ; is executed after the usurping module test_v1.pm has been loaded

        as demonstrated you can use Foo::Bar as soon as the BEGIN block sets %INC.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

        update

        °) well if the module is written in a way that BEGIN blocks expect to be run after the use (i.e. in the inherent do FILE), then yes could get bitten by order problems. The OP didn't sound like his intention was to simulate meta programming magics.

        Otherwise he should do a BEGIN { eval $MODULE_CODE; package->import() }

        Please note that this code could be encapsulated in a special module.

        Something like use FromData "__MODULE_NAME__" could extract the Modules_Code from the DATA section (delimited by __MODULE_NAME__ ) and import it into the upper namespace.

        ... or ...

        Another - probably more elegant - option is to provide a loader function as value in %INC . use would evaluate this function to get the MODULE_CODE.

        You can also insert hooks into the import facility by putting Perl code directly into the @INC array. There are three forms of hooks: subroutine references, array references, and blessed objects.

Re: Multiple Packages in a Module?
by haukex (Archbishop) on Jun 27, 2018 at 10:50 UTC

    If you want to play it safe and check that a package contained in test_v1.pm isn't loaded by a use package; statement somewhere else, insert the following code at the top of the test_vN.pm files. This won't protect you against loading more than one test_vN.pm file, but if that does happen, and you have warnings enabled (which you always should, of course), you should see some "Subroutine redefined" warnings (Update: I hope that test_v1.pm and test_v2.pm defining at least a few of the same subs is a safe assumption).

    use Module::Runtime qw/module_notional_filename/; BEGIN { for my $package (qw/ Foo Foo::Bar /) { my $fn = module_notional_filename($package); die "$package already loaded from $INC{$fn}" if exists $INC{$fn}; $INC{$fn} = __FILE__; } }
Re: Multiple Packages in a Module?
by Anonymous Monk on Jun 25, 2018 at 18:28 UTC
    If foo.pm defines both package Foo and Package Foo::Bar, all will be well if every program does use Foo; that is never, ever preceded by use Foo::Bar;, and if there is not by pure-accident a Foo/Bar.pm file anywhere in your filesystem. If there's a conflict in your package resolution, it can be a true headache to debug. Worse yet, your test cases might not be doing what you think they are!
      all will be well if every program does use Foo; that is never, ever preceded by use Foo::Bar;

      I disagree with the two underlined wordings. It's possible that the two definitions of package Foo::Bar don't conflict, and that the overlap may even be intentional, for example if package Foo needs to monkey-patch something into Foo::Bar. Granted, these cases may be rare, and the point that the packages might conflict is very valid, but the above wordings are just too absolute for my taste.

      If foo.pm defines both package Foo and Package Foo::Bar, all will be well if every program does use Foo; that is never, ever preceded by use Foo::Bar; ...

      In the above case, a  use Foo::Bar; statement can never access a file  foo.pm (In Windose (sigh)) or  Foo.pm (in other, saner file systems).

      ... and if there is not by pure-accident a Foo/Bar.pm file anywhere in your filesystem. If there's a conflict in your package resolution ...

      If there's ambiguity or conflict in file name resolution ordering, then any use or require statement can latch onto the wrong .pm file; it has nothing to do with defining multiple package namespaces within a single .pm file.

      ... it can be a true headache to debug.

      True that, brother!


      Give a man a fish:  <%-{-{-{-<

A reply falls below the community's threshold of quality. You may see it by logging in.