Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Can't call sub within same package in BEGIN block

by exilepanda (Friar)
on Aug 14, 2014 at 06:38 UTC ( [id://1097384]=perlquestion: print w/replies, xml ) Need Help??

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

# MyStuff.pm package MyStuff; use strict; BEGIN { _init() } sub _init{ .... } 1;

This won't work and saying undefined subroutine etc etc

I tried use subs "_init";, tried to call it MyStuff::_init() in the BEGIN block, but both failed. The only workaround is to put the sub _init above the BEGIN block, and it works.

Why is that? And is that anyway I can make that sub below the BEGIN block ?

Replies are listed 'Best First'.
Re: Can't call sub within same package in BEGIN block
by AppleFritter (Vicar) on Aug 14, 2014 at 09:30 UTC

    As others have pointed out, this is simply how BEGIN blocks work -- indeed, how they are defined to work. Here's what perlmod says in the BEGIN, UNITCHECK, CHECK, INIT and END section:

    A BEGIN code block is executed as soon as possible, that is, the moment it is completely defined, even before the rest of the containing file (or string) is parsed. You may have multiple BEGIN blocks within a file (or eval'ed string); they will execute in order of definition. Because a BEGIN code block executes immediately, it can pull in definitions of subroutines and such from other files in time to be visible to the rest of the compile and run time. Once a BEGIN has run, it is immediately undefined and any code it used is returned to Perl's memory pool.

    Emphasis mine.

    This shows why the subroutine has to be defined before the BEGIN block is encountered; when that block is run, the rest of the file, as far as Perl is concerned, does not even exist.

    And it cannot be any other way, since one of the reasons for having BEGIN blocks is that they may affect the way the rest of the file is parsed. Source filters would not be possible without BEGIN blocks working this way.

    If you need to ensure that certain code is run before other code, but only after the file has been parsed (so that later subroutine definitions etc. are available), use a UNITCHECK, CHECK or INIT block. Quoting the same perldoc again:

    UNITCHECK, CHECK and INIT code blocks are useful to catch the transition between the compilation phase and the execution phase of the main program.

    UNITCHECK blocks are run just after the unit which defined them has been compiled. The main program file and each module it loads are compilation units, as are string evals, run-time code compiled using the (?{ }) construct in a regex, calls to do FILE, require FILE, and code after the -e switch on the command line.

    BEGIN and UNITCHECK blocks are not directly related to the phase of the interpreter. They can be created and executed during any phase.

    CHECK code blocks are run just after the initial Perl compile phase ends and before the run time begins, in LIFO order. CHECK code blocks are used in the Perl compiler suite to save the compiled state of the program.

    Inside of a CHECK block, the value of ${^GLOBAL_PHASE} will be "CHECK".

    INIT blocks are run just before the Perl runtime begins execution, in "first in, first out" (FIFO) order.

    Inside of an INIT block, the value of ${^GLOBAL_PHASE} will be "INIT".

    The CHECK and INIT blocks in code compiled by require, string do, or string eval will not be executed if they occur after the end of the main compilation phase; that can be a problem in mod_perl and other persistent environments which use those functions to load code at runtime.

    You'll also get the added benefit of confusing those who'll read your code later on. :) Everyone knows about BEGIN, but whoever heard of UNITCHECK? (I hadn't, until quite recently.)

      That's a good one and thanks for pointing out! I didn't really realize I can use those blocks in this way. Though, what confused me was about use vs BEGIN that I think use subs ... could force perl to read for the rest to find this symbol first. But if say it just mean sub mysub;, and BEGIN will run that ASAP, then I can understand why it happens that way.
Re: Can't call sub within same package in BEGIN block
by Anonymous Monk on Aug 14, 2014 at 06:47 UTC

    Why is that?

    Because that is the way it works, you can't call a subroutine until it is defined, the BEGIN block runs before the subroutine is defined. See BeginCheckInitEndUnitcheck.pl

    And is that anyway I can make that sub below the BEGIN block ?

    Why do you think you need a begin block? Because you don't you know :)

      Thanks, I am using the BEGIN block to do some  require ... import ... stuffs according to the $ENV situation, as well as establish some more $ENV variables.

      However I also provided some overloading condition to be made on the %ENV by other modules, so the contents inside the BEGIN maybe need to rerun (and some extra modules maybe import), thaz why I pulled the codes out from BEGIN and form a sub (coz BEGIN can't export), so BEGIN and other modules can share the same operations.

      What I still don't understand is why use subs lost this race. Say:

      use strict; use subs "TestMe"; BEGIN { $x = 10; TestMe(); } sub TestMe {"Test fine"}
      strict can effective immediately and alarm $x was not declared, but why not subs ?

        Thanks, I am using the BEGIN block ... instead of creating another module

        You don't need a BEGIN {} block to import/export or touch %ENV (icky like a code smell potentially almost), just simply make another module :) do stuff in import, it takes args

        use Local::TouchEnvFirst; ## import use Local::TouchEnvSecond -optionFantastic; use Local::TouchEnvSecond -optionNotFantastic; no Local::TouchEnvFirst; ## unimport no Local::TouchEnvSecond -tooComplicated; use Local::Madness -init => sub { warn "if you gotta run me, go ahead :)"; };

        Does the light bulb turn on? :)

        What I still don't understand is why use subs lost this race. Say:

        Same reason, it doesn't define the subroutine, its a forward declaration, its exactly like this literal code

        sub TestMe; BEGIN { TestMe } sub TestMe { warn "oh no you didn't" } __END__ $ perl testme Undefined subroutine &main::TestMe called at testme line 2. BEGIN failed--compilation aborted at testme line 2.

         

        rm -rf goners

        What I still don't understand is why use subs lost this race.

        It didn't. use subs did its thing before the BEGIN block was executed.

        You're trying to execute "Test fine" before it even gets read from the source file by the parser!

Re: Can't call sub within same package in BEGIN block
by Anonymous Monk on Aug 14, 2014 at 07:43 UTC
    BEGIN blocks are executed during compilation, as soon as they are seen. Since the compiler hasn't seen the body of _init, it has nothing to execute. Declare your sub before the BEGIN and it should work. Otherwise, I think you might have an XY Problem: could you describe in more detail what your BEGIN is going to be doing, or even post the actual code? We can probably find a good solution that does't require some hackery. (also I'm a little confused by what you mean with "BEGIN can't export", since actually BEGIN is crucial in the export/import process)
      Declare your sub before the BEGIN and it should work.

      No, it won’t:

      18:37 >perl -Mstrict -wE "use subs 'TestMe'; BEGIN { TestMe(); } sub T +estMe { say 'Hi!'; }" Undefined subroutine &main::TestMe called at -e line 1. BEGIN failed--compilation aborted at -e line 1. 18:37 >

      As Anonymous Monk pointed out above, you have to define the sub before the BEGIN block:

      18:37 >perl -Mstrict -wE "sub TestMe { say 'Hi!'; } BEGIN { TestMe(); +}" Hi! 18:41 >

      In Perl, as in C, it is important to keep in mind the distinction between declaration and definition.

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Hadn't had coffee yet, thanks for the correction :-)
        ... the distinction between declaration and definition.
        Hey I really love this one!! A very neat comparison. New lesson learned!!

        Anyway I can get what Anonymous Monk means in the another reply that said "you can't call a subroutine until it is defined "

        Cheers

Re: Can't call sub within same package in BEGIN block
by Laurent_R (Canon) on Aug 14, 2014 at 20:36 UTC
    Hi, I am not sure that I fully understand what you are trying to do, so I am not sure it would really help solving your problem, but it seems to me that you could possibly define an anonymous subroutine reference within your begin block to get what you want

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (6)
As of 2024-04-18 01:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found