Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Stupid question

by code24 (Initiate)
on Dec 07, 2000 at 02:29 UTC ( [id://45356]=perlquestion: print w/replies, xml ) Need Help??

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

Okay, I know that this is a really easy one, but it's been bothering me. I've looked at all kinds of docs, but I can't seem to find an answer to this. I'm sure I'm looking right past the answer, and not seeing it.

How do I scope a variable so it can only be seen in the 'main' section? I _thought_ that 'my' was supposed to handle this but I guess not in the way I was thinking.

Exmple code:

#!/usr/bin/perl -w use strict; sub foo(); my $bar = "I can see you"; &foo; sub foo() { print "inside foo: $bar\n"; return(0); }

Replies are listed 'Best First'.
(tye)Re: Stupid question
by tye (Sage) on Dec 07, 2000 at 21:45 UTC

    Nearly every Perl script I write follows this format:

    #!/usr/bin/perl -w use strict; use Other::Modules qw< ImportedFunction >; use vars qw< $Globals >; Main( @ARGV ); exit; sub Routine { ... } BEGIN { my $static= "Initial value"; sub usesStatic { ... } } sub Main { my $putMainVarsHere; ... } __END__
    So you can prevent your subroutine from accessing your "main" variables by putting them where I wrote $putMainVarsHere.

    This style has evolved for quite a while and there are specific reasons for almost every aspect of it. I find it catches quite a few errors for me.

    I was going to explain my reasons for most aspects of this style, but I decided that that would be more appropriate elsewhere.

    (updated twice)

    Update: Here are some justifications for some of my decisions that I collected quite a while ago planning to write this up more formally. Instead I've just moved them from my scratchpad to here:

    • #!/usr/bin/perl is the standard place for Perl as declared by Larry himself. Even if you install /usr/local/bin/perl, please also put a link in /usr/bin and please use #!/usr/bin/perl in scripts that you make public
    • Explicitly listing what you want exported from a module means that when I read your code and see GnarfleTheGarthock(), I can search for "GnarfleTheGarthock" and I'll find the "use Coneheads qw( GnarfleTheGarthock );" line and know where to go find documentation and/or code for that subroutine even if Gnarfle or Garthock appear to be an "obvious" Coneheads reference to _you_ ;)
    • Lets you distinguish file-scoped lexicals declared at the top of your scripts that act as global variables from "sub Main" lexicals that should not be directly accessed by other subroutines.
    • Gives you a handy place for a break point ("c Main") when debugging (stepping over module and global variable initialization can get boring)
    • Since Perl's "global destruction" phase doesn't obey reference counts, it can cause problems when objects refer to each other. "sub Main" can aid debugging of such destruction issues because you can break after returning from Main()...
    • Main(@ARGV) allows you to parse the command line via @_ which makes it easier to refactor that code later
    • exit; means that no code outside of a sub below will get executed. This can bite you, but I really like it because I know I don't have to search the entire script looking for random bits of code.
    • That previous item also forces you to use BEGIN to initialize any "static" lexicals, which ensures that you never call a subroutine before any "static" lexicals that it uses are initialized
    • The BEGIN block also prevents "will not stay shared" warnings that break the use of file-scoped lexicals in subroutines with Apache::Registry under mod_perl

            - tye (but my friends call me "Tye")

      Posted only to maybe help someone else at some point in time...

      Update: It's all about this Carp bug (w/this solution) and modifying global variables ( @ARGV in this case - GetOptions modifies @ARGV ) after they get passed to functions.

      Although I agree with this format (but have mostly not gotten in the habit of using it myself), one of my co-workers was doing something similar, and got the following error during a Carp::confess from deep within the code (perl 5.8.8 - and we could not come up with a simple test case):

      Bizarre copy of HASH in sassign at ...../Carp/Heavy.pm line 45

      Although it seemed to be a completely unrelated part of the code, moving the Getopt::Long::GetOptions() call from inside to outside of Main() fixed the problem, and we got the proper confess with stack trace that we wanted to see. Though I'm sure when something with this much deep magic goes wrong, we've probably only fixed a symptom of the problem :-)

        Yes, those kind of errors are where any random change can prevent / postpone the error. For example, running under the Perl debugger is frequently one way to make the error go away. This can be a convenient way to get the stack trace and it can be very inconvenient in preventing you from being able to use the debugger to get more information. I've also often seen turning up trace or adding "debug prints" make such bugs no longer reproduce.

        But such voodoo changes usually just postpone the problem for a while.

        I'm glad you got the call stack and were able to use that to debug the problem that was causing Carp to be called. But if I were in your shoes, then I would be worried of other things eventually causing more "bizarre copy" panics.

        In such cases, I usually look for a minor Perl upgrade, upgrades for XS-using modules, or elimination of the use of some XS-using modules.

        Also, these bugs are much more likely in an environment like a Perl daemon or like mod_perl where you have an interpreter instance that runs for quite a long time doing tons of things. Re-spawning the daemon periodically or configuring mod_perl to restart children a bit more often can reduce the frequency of such bugs appearing.

        - tye        

      tye,

        #!/usr/bin/perl is the standard place for Perl as declared by Larry himself. Even if you install /usr/local/bin/perl, please also put a link in /usr/bin and please use #!/usr/bin/perl in scripts that you make public

      Please consider in the *nix world of today, you may make your system useless if you do the above. Changing the default system "perl" is quite dangerous. 5 years ago, I would agree, but today it's better to link the other way around if you're not installing a different Perl. Just my 2 cents.

      Thank you

      "Well done is better than well said." - Benjamin Franklin

        All I suggested is that there be a /usr/bin/perl (if there is any version of Perl installed). I don't see how there being a /usr/bin/perl would make a modern Unix system "useless".

        - tye        

        Whichever direction you link it, that would seem to present a problem. If you're installing a separate perl because you want to leave the stock perl pristine for the OS to use, well, the OS will expect to find the stock perl in /usr/bin/perl. If /usr/bin/perl is a link to /usr/local/bin/perl (or wherever), then the OS will be using your local version, so you might as well have just replaced the stock version with your own version, right?

        Another thought: if you symlink /usr/bin/perl to somewhere else, can you trust that an OS upgrade of the stock perl won't clobber your symlink?

        Aaron B.
        Available for small or large Perl jobs; see my home node.

Re: Stupid question
by chipmunk (Parson) on Dec 07, 2000 at 02:37 UTC
    By main section, you mean 'code not in subroutines', right? As you've discovered, Perl considers subroutines to be part of the block in which they were defined, so they have access to all the lexical variables of that block.

    If you want to limit the scope of a lexical variable, you need to declare it inside a block, like this:

    #!/usr/bin/perl -w use strict; sub foo(); # main: { my $bar = "I can see you"; &foo; } sub foo() { print "inside foo: $bar\n"; return(0); }
    With use strict, that code doesn't compile, because the $bar in foo() hasn't been declared, which is the effect you want.
      Thanks, that helps.

      Is that considered a 'good' format, or should I just not worry about the subroutines access to these variables?
        I personally think that 'main' variables, while able to be accessed by subroutines declared in the same package, are safe enough, as long as you don't access them directly in a subroutine.
        use strict; my $foo = 'yo'; &bar($foo); print "$foo\n"; sub bar { my $bar = shift; $bar .= ' dude'; }
        Declare you variables in main, just don't use them directly in your subroutines and you'll be okay.

        Jeff

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        F--F--F--F--F--F--F--F--
        (the triplet paradiddle)
        
        If you are worried about the subroutines accessing these variables, this is the way to avoid the problem; go for it! It is a good programming practice to have subroutines get all their input through the argument list and not through external variables, because it makes the code more maintainable.
(jeffa - variable scope) Re: Stupid question
by jeffa (Bishop) on Dec 07, 2000 at 02:36 UTC
    One way would be to put all things related to $bar in it's own block:
    { my $bar = "I can see you"; }

    Jeff

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    F--F--F--F--F--F--F--F--
    (the triplet paradiddle)
    
Re: Stupid question
by arturo (Vicar) on Dec 07, 2000 at 02:44 UTC

    How about making it all explicit, put $foo in its own package, and just be judicious in your use of the full name of $foo.

    #!/usr/bin/perl -w use strict; package Foo; use vars qw/$foo/; $foo = "bar"; package main; # always use $Foo::foo from here on down

    Or you could exploit the fact that my variables scope, while it stretches across packages declared in the same file, does not reach outside of the *file* in which they are declared. I.e. if in foo.pl you declare $foo with my, then that variable won't be visible inside subroutines declared in bar.pl

    A little more souped-up kind of idea might be to define an *object* with accessor methods. Syntactically annoying (which I gather is what you're trying to avoid =), but IMO, conceptually quite clean.

    final note: please, use a more descriptive title ... it might help others trying to solve the same problem =)

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2024-04-19 11:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found