Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Autovivification with require

by Bod (Parson)
on Nov 19, 2020 at 22:53 UTC ( [id://11123854]=perlquestion: print w/replies, xml ) Need Help??

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

Esteemed Monks

Thanks to the The Monastery, I today found out about Perl::Critic so thought I would throw a recently written, small (600 lines) Perl script at it just for fun...so I used the website tool knowing that it would complain that scriptures were off.

It has highlighted 12 issues amongst them:
"require" statement with library name as string at line 10, column 1. Use a bareword instead.

I've looked this up and understand what it is suggesting I do

require "incl/common.pl"; # What I've got require incl::common.pl; # What Critic suggests I have

I've consulted the documentation for require and I understand the portability advantage of the slash being the other way on some systems. As my code will never be run in another environment, I don't see the advantage of backdating the code for this reason but going forward, that makes sense.

But then we get to autovivification...
Off to the Wikipedia Article as this is a term I have heard but always shied away from...time to properly understand. I think I now sort of understand but certainly not well enough to articulate it to myself let alone explain it to anyone else.

I do understand that the syntax I have used for require will not autovivify whereas the version suggested by Critic will. What I certainly do not understand is how this makes any practical difference in any way. Whether the magic happens at compile time or run time surely doesn't matter here as the code only runs once for each executed instance; which equates to someone (or some machine) requesting a page from the webserver. I totally get that in many cases there are major differences between compile and run time - but here in a require statement?

Does it actually matter in practical terms or is autovivification in this example just a theoretical matter here?

Replies are listed 'Best First'.
Re: Autovivification with require
by Fletch (Bishop) on Nov 19, 2020 at 23:11 UTC

    This has nothing to do with autovivification. That term means perl automatically treating undef in an lvalue reference context as evaluating to a reference to a newly created item of the correct type (arrayref or hashref); see perlref for the definition.

    The suggestion is to use a bareword package name instead of a string path and is saying that best practices would be to write a module (which would be contained in the file incl/common.pm) and then use require incl::common; (no extension) and let perl locate the file in one of the directories listed in PERL5LIB (see perlrun and perlvar for more on that).

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      From the documentation for require

      In other words, if you try this:

      require Foo::Bar; # a splendid bareword
      The require function will actually look for the Foo/Bar.pm file in the directories specified in the @INC array, and it will autovivify the Foo::Bar:: stash at compile time.

      But if you try this:
      my $class = 'Foo::Bar'; require $class; # $class is not a bareword #or require "Foo::Bar"; # not a bareword because of the ""
      The require function will look for the Foo::Bar file in the @INC array and will complain about not finding Foo::Bar there. In this case you can do:
      eval "require $class";
      or you could do
      require "Foo/Bar.pm";
      Neither of these forms will autovivify any stashes at compile time and only have run time effects.


      This seems to imply that there is some implicit advantage of one form over another because one autovivifies and the other does not - it was this part of the documentation that gave rise to my question...

        I believe what that usage is trying to highlight is that if you have a bareword use Foo::Bar; or require Foo::Bar; then at compile time perl knows that there is a Foo::Bar package and that it will make some kind of entries in the package stash (*Foo::Bar and/or %Foo::Bar::) before continuing to compile subsequent code (which may affect things like "variable only used once" warnings). Also in the use case the import method would be called at compile time which would then have side effects (e.g. inserting entries in the calling package's namespace).

        If you use the string version the code in the file pulled in won't be compiled until the require statement is actually executed which will be after everything else has already compiled. It affects when some things happen; whether that's an "advantage" or not depends on what you're expecting to happen.

        ## Poor example: Cwd pulled in with use so %Cwd:: is fully populated $ perl -E 'say qq{Cwd stash keys: }, scalar %Cwd::; use Cwd; say qq{Cw +d stash keys after use: }, scalar %Cwd::; say qq{Config stash keys: } +, scalar %Config::;require "Config.pm"; say qq{Config stash keys afte +r requiire: }, scalar %Config::;' Cwd stash keys: 39 Cwd stash keys after use: 39 Config stash keys: 1 Config stash keys after requiire: 20

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

    PM vs PL
    by Bod (Parson) on Nov 20, 2020 at 16:00 UTC

      I was under the impression that best practice is to name files to be brought in with require as *.pl and those being brought in with use as *.pm. I have no idea where I got this notion from, it was a long time ago - at least 2 decades - and I have been doing it that way ever since.

      Almost every script I write has a require or three at the top for bringing in code I have created. Most have a few use statements for other people's modules. Over the years I have created a handful of OO modules which get included with a use statement.

      Have I been doing this wrong all these years or has best practice changed and I never noticed?

        I came late to the Perl game, but I've met required pl files in very old code. So I'd guess the best practice has changed.

        Update: I even discussed it here.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

        I would like to mention a 3rd type of "include": a PMC file which is "compiled" Perl code, see e.g. Module::Compile (or: what's this PMC thingy?). (Edit: also this is of interest Perl v5.6.1 looking for .pmc files)

        I don't know whether this project is stil alive but I just confirmed that if a Test.pmc file exists alongside an Test.pm , use Test; will attempt to load Test.pmc first (question: can they be in the different INC dirs?).

        Caveat: Because my systems's module compiler (perl -MO=Bytecode,-H -MTest -e1 > Test.pmc) fails with: Can't locate object method "ix" via package "0" , I just touch Test.pmc and surely I get a segfault upon running my script as expected from loading rubbish. The important thing is (i believe) an attempt to load Test.pmc is done. When I delete Test.pmc all runs well, the Test.pm is loaded.

        bw, bliako

      Maybe another way to say it is that, if someday you needed to rearrange the source file structure for any good reason, but you used literal-string names containing file and directory names, you'd have a lot of tedious text changes to do throughout the code. Whereas you'd have a lot more flexibility and a lot less work to do if you'd referred to them as modules and let Perl find them for you.
Re: Autovivification with require
by perlfan (Vicar) on Nov 20, 2020 at 19:48 UTC
    Is there a reason you can't convert incl/common.pl to a module (lib/Common.pm)? Using the modulino approach, all but the most stubburn old school .pl "includes" can be made easily accessible as a library and via use. Not saying there are not good cases for require, there most certainly are. If you want to convert common.pl to a module, I'm happy to address any questions in a different thread.

      One include file that most of my scripts use is require "incl/html.pl"; in various forms. Because the vast majority of what I write is web code, this include has subroutines to supply common parts of the web page: header, footer, Javascript routines, etc. So it is really nothing more than a collection of subroutines. Others do common stuff like connect to the database schema(s) and provide logging functionality, etc.

      I usually create two copies of each website depending how complex it is going to be functionally - prod and test. They have identical code except for a incl/variables.pl file which has variables containing database credentials and environment settings like whether or not to send out automated emails.

      So converting them to modules should not be too complex WRT the content. But there are a number of *.pl include files some of which then require another.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (4)
As of 2024-04-25 14:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found