|Do you know where your variables are?|
'our' is not 'my'by Ovid (Cardinal)
|on Aug 16, 2001 at 21:05 UTC ( #105446=perlmeditation: print w/replies, xml )||Need Help??|
Update: At the request of a couple of people, some as replies to this node and one as a private message, I have added a link to this node from the Tutorials section. I have linked it rather than repost it to tutorials as there is no reason for me to get the rep twice. I might add that I would not have done this were it not for the high rep this node has received (thus suggesting to me that monks find this information useful).
Many people misunderstand how our is used and I've often seen code where it's used as a synonym for my. This is not the case and I hope this clears up some of the differences. I'm posting this because I've run across this a few times lately and I thought I should just 'get it out there' for others.
There are basically two ways of declaring variables in Perl: global and lexical. A global variable has a package name prepending to it:
All packages can access the variable $foo in the main symbol table (%main:: or the shorthand %::) by using $main::foo. Global variables are generally a bad idea, but do crop up in a lot of code.
A lexically scoped variable is declared with my:
Though they look similar to the package variables above, they are not the same and cannot be accessed outside of the scope in which they were declared.
If you use strict and you try to access a variable that's not previously declared, you'll get an error similar to the following:
Basically, Perl expects that you are trying to use a package variable, but left off the package name. The problem with using package names ($main::foo) is that strict ignores these variables and if you type $main::field in one place and $main::feild in another place, strict won't warn you of the typo.
our tries to help out by letting you use package variables without adding the package name. The following two variables are the same:
That eliminates the $main::f(ie|ei)ld problem by allowing you to do this:
From there, you just drop package names and the script will run and later Perl will kill the script if you try use $feild. Unfortunately, there are a lot of problems with this.
The main problem is that you are now using global variables which is generally a Bad Thing. Any code, anywhere, can change your global. Large systems with lots of globals typically suffer this problems and quickly become unmaintainable.
This also leads to very subtle coding errors. Try the following code:
That will print 1, 2, and 3 on successive lines. Change the our to a my and it prints 1, 1, and 1. Because the my variable goes out of scope at the end of the sub and the our variable doesn't (since it's global), you get wildly different results.
There is one use I have found for our variables. if I am forced to maintain a large code base with lots of global variables, I usually expect to see lots of code problems, including misspellings of those globals that are overlooked due to the hacked nature of those code. For example, one site I worked on had some object-oriented code that stuffs a lot of data into package main (you can keep reading after you stop laughing). I've seen code like this:
I know, it doesn't make a lot of sense (why doesn't it just return the line items instead of the SQL?), but I needed to clean up stuff like that. So, in my code, I put the following:
I didn't convert them to my variables because these variables were being declared in another package.
This doesn't seem like much benefit aside from eliminating a few bytes (and it certainly wasn't the best solution, but we've all had time constraints...). However, there is a huge benefit. Later in the code, when we encounter $main::slq, it becomes $slq and the program dies a much needed death.
As a side note: the primary difference (that I can see) between our and use vars appears to be that our lexically scopes the use of the variable name (not the variable) and the other does not. The following code snippets should clarify that:
That code will work fine. However, change the use vars qw/ $foo /; to our $foo and Perl dies, telling you that $foo requires an explicit package name. However, change the print statement to include the package name and all is well:
This behavior may not seem immediately useful, but you can then use an our declaration at the top of a subroutine, strip off the package names in the sub, and not worry about this conflicting with my variables in the rest of the code.
Change that code to use "use vars" and you'll see it print 7 twice, stepping on your my variable.
Interesting to note that the only uses I've found for our have been hacking bad code...
Vote for paco!
Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.
Back to Meditations