Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Are global variables "bad"?

by jpearl (Scribe)
on Apr 21, 2009 at 16:13 UTC ( #759043=perlquestion: print w/ replies, xml ) Need Help??
jpearl has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing a ptk application to visualize some genomic data we are working with. I read in a file and store a variety of information in a HoA about strains of bacteria etc. However, the file is rather large and it takes a minute or so to parse, so I would like to only do that once.

Currently the HoA and a couple other data (i.e. # of strains, array of strain names) are stored as (references to) global variables. I have a getter and setter function which currently is the only one that directly interacts with the variables. I always have a queasy sort of feeling when using globals, a lurking suspicion in my mind that eventually this will come back and bite me in some mysterious and underhanded way.

Several functions in the program use this data, and so I do want "global" access. None of the functions change the actual original HoA et. al. once the intial file read is done. So, my question boils down to: is it a bad idea to have global variables? If it is how can I encapsulate them in perl so I can pass handles around to the functions that need the data? Or is having getter and setter functions "safe" enough?

Thanks!

~josh

Update:Just wanted to say "thank you" for all the great comments and links for further study. PMs is definitely in a league of its own for considerate first-rate responses.

Comment on Are global variables "bad"?
Re: Are global variables "bad"?
by samtregar (Abbot) on Apr 21, 2009 at 16:32 UTC
    What you're looking for is called a "singleton" - an object which naturally has a single instantiation which is shared by everyone that needs it. This is common for DBI handles for example. You might take a look at Class::Singleton for a tiny bit of implementation help but personally I'd just code the interface myself.

    As to whether it's "bad" - no, but it does come with some danger. In the case of DBI handles the fact that they're often shared by disparate modules can make agreeing on DBI options hard - one part needs a particular RootClass, another requires RaiseError to be off, etc. It can cause "action at a distance" problems too.

    -sam

      I have read a bit about singletons, though to be honest my only exposure was basically checking out globals and why they might be bad. For instance when reading this there was a link to this. Kind of a programming culture of fear :-P

      Thank you for the links, I'll definitely check out Class::Singleton Though you are probably right on just creating my own solution.

        I don't actually see any particular reason to use a singleton in this case: You are parsing a file, and want to store it someplace. There is no particular reason (from what you've said) why parsing a different file, or even reparsing the file should be prevented. (Besides the fact of memory useage/speed, which to me isn't a reason for a singleton, just a reason to keep using the same object.)

        Basically, it's extra complexity to prevent future uses. I don't see why either would be something you want.

        If one expects the file format to change over time to accommodate new research data or evolving use cases, using class methods or a singleton object would be a good idea. OOP architecture makes it much easier to support multiple file formats in parallel.

        Without objects, you have three options for supporting two file formats:

        1. Define a second set of parsing functions with ugly names like parse_blah2. Then go all over your existing code inserting if...else statements to select the right set of functions.
        2. Define the functions for format II in a new package. Now go back to the consumer code and replace all of your calls to parse_XXX(...) with eval("${sParserPackage}::parse_XXX").
        3. Define a second set of parsing functions and create a dispatch table. Now go all over your code and replace the direct calls to parsing functions with calls to dispatch table members. You've gotten rid of the bulky if...else and eval(...) statements, but you are still making massive code changes. You've also lost some debugging ease since dispatch table methods are going to show up in standard debugging tools as anonymous code references rather than named functions. You simply aren't going to get error messages like Can't locate object method "parse_blah" via package "MyModules::Parse2" Instead you'll get an obscure message about Use of uninitialized value in subroutine entry

        Or you could define your parsing process in an object. The only change that would need to be made in your program would be a small bit of code to check the version number and retrieve the correct parser. Any other code using the parser methods would stay the same since it only cares that statements like $oFoo->parse_blah and $sParserClass->parse_blah() behave in a predictable fashion.

        Classes and objects, even singleton objects, give one the ability to override and redefine methods without changing a byte of consumer code.

        Best, beth

        Explanatory note: in Perl, a class method is just a package subroutine whose first parameter is a class name. Calling Foo->fribilate(...) tells Perl to pass "Foo" as the first parameter and to allow overloading. It also lets one use a variable for the package name. ${somevar}::fribilate(...) generates complaints about bare words where operators were expected. That is why we had to use eval(...) in non-object option 2.

      I like the singleton idea. I've used it for DBI connections and config files and it makes it easy to have global behaviour without the downsides of having global variables. At least thats how I see it.


      ___________
      Eric Hodges

        After having worked with code that had made extensive use of Singletons, I think I would have to disagree. I'd say that Singletons have all of the advantages of global variables with all of the disadvantages of globals.

        The only advantage I've seen is in grouping a set of variables (or behavior) into one Singleton object, instead of scattering them around. But, you can do that without Singletons, as well.

        I try to avoid Singletons, because they've been the source of lots of trouble for me.

        On the other hand, in a standalone script global variables can often simplify the code. The practice does tend to put an upper limit on how much the code can safely do.

        G. Wade
      Maybe I'm missing something, but how does wrapping a $dbh handle in a singleton avoid action at a distance? If you've got code flipping AutoCommit on and off don't you have the same potential problems?
        It doesn't. I was mentioning it as a problem inherent to singletons.

        -sam

Re: Are global variables "bad"?
by DStaal (Chaplain) on Apr 21, 2009 at 16:36 UTC

    Your concerns about having them come back and bite you are probably warranted, in my experience. While it is certainly possible to write and maintain good code that uses global variables, it takes a level of dedication to self-discipline, and a good strategy to keep them separate from any other variables you may be using at any particular point in time.

    Since you can create 'setter' and 'getter' functions for them, how about putting them in an object? Then you can pass the object around, and it will take less dedication to preserve the encapsulation around the variables.

    There are several ways to do objects in Perl, depending on how much you want to do and how complex you want the result. I've heard good things about Moose, although I've yet to use it. (I normally work directly with inside-out objects, to reduce the number of dependencies.)

      Thanks DStaal,to be honest, coming from a java background, I definitely am drawn towards making this object oriented. Actually creating the getter and setter functions were definitely an attempt to emulate OO style programming in my (currently) almost completely functional style program. I certainly am eying perl OO surreptitiously, but then I'd have to figure out how bless works. Though, I suppose that's probably going to have to happen soon anyway.

        Quick version on bless: It marks a reference as an object. The reference can be to any type of varible: scalar, array, hash, are all common. You will need to figure out some way of accessing the data you want to access as the object's data based on the contents of that variable.

        Common strategies include: just putting it in the scalar (for simple data), or array/hash (for more complex); and putting a key into some other data structure into the variable (inside-out uses package-level hashes, with the key being the blessed variable's address. Some people like to use arrays, for faster access.).

Re: Are global variables "bad"?
by jettero (Monsignor) on Apr 21, 2009 at 16:39 UTC

    Globals are a pretty common thing in modules. There's a lot of really good reasons to use package globals and package namespace globals. If your script is just a collection of functions, then they're acting a bit like a module called "main."

    On one extreme, you end up passing the same (essentially) global data from function to function, just to avoid a global variable ... on the other extreme, every variable in the program is global. Extremes can be bad. Global variables aren't necessarily bad.

    -Paul

Re: Are global variables "bad"?
by ELISHEVA (Prior) on Apr 21, 2009 at 16:59 UTC
    Globals aren't necessarily bad, but they can make growing your program difficult. Suppose you have three functions that work as a group to parse hash X. All is well and good when you are only loading data from one data set into your HoA.

    But suppose you one day want to compare HoA's drawn from two or more data sets? What will you do then? Because your functions expect their data in the one and only global variable you are a bit stuck. The problem isn't unsolvable, but it is going to be ugly. You could:

    • run the functions as is, copy data from the global; clear the global; and then run them again with the new data set.
    • refactor your code into objects - each object has its own hash. The functions become methods that operate on the object's hash.

    However, if, at the very start, you had defined your data and three functions using an an object, then you would have gotten all the benefits of a global variable and avoided the downsides of global variables should your needs expand.

    Best, beth

      Hmm. This is good to know, and the impression I'm getting from all the comments is that its probably going to be a good idea to take a crash course in OO perl. The program isn't super huge (maybe 300 lines) but I keep having ideas for additional functionality I'd like it to have. And this is even before the users get a look at it. Even as it is, one of the main reasons for this question was that I started to realize if I wanted some of my ideas implemented I was going to need to increase the number of global variables, and then how I'd need to change the current logic of the program to handle the added functionality and.... etc.

      Thank you for the comments, everyone has been extremely helpful.

        Don't forget to have fun. If this is a learning project or something just for you, don't worry too much about correctness. Just have a good time and pay attention. If you need to refactor later, that can also be fun and a learning experience. Trying to get something perfect up front can be a bit paralyzing and counterproductive. Treating first stabs as prototypes can dig out the real issues you'll face for a robust final version.

        There is a great quote in the intro to a chapter of one of the Perl books (which I don't have in front of me). Something along the lines of: It is faster to build a four inch telescope and then a six inch telescope, than it is to build a six inch telescope.

        Learning the basics of OO perl isn't hard. While there are esoteric aspects that take longer to learn and understand, the basics are well presented in perlboot, perltoot, perltooc and perlbot, which you can read in a few hours, and there are several articles in Object Oriented Programming which provide an easy introduction to simple objects. With a Java background, I expect you would pick up Perl objects and have a significant return on your investment quite quickly.

Re: Are global variables "bad"?
by Your Mother (Canon) on Apr 21, 2009 at 17:07 UTC

    What ELISHEVA said. I'd go further and say yes, they are bad; not always but more often than not. As samtregar mentioned, they often lead to action at a distance and that leads to debugging difficultly which leads to 100 wasted hours over time because the original dev wanted to save 1 or 2 up front by using a global or a singleton instead of something reusable.

Re: Are global variables "bad"?
by JavaFan (Canon) on Apr 21, 2009 at 20:01 UTC
    So, my question boils down to: is it a bad idea to have global variables?
    There are several answers to your question. Let me just give a few remarks.
    • First of all, "global variables" is not a term everyone uses to mean the same thing. Many Perl programmers use the term "global variable" to mean "package variable". But in general, a "global variable" means a variable that's "visible" or "accessable" from everywhere. A lexical variable defined at the file scope can also be a global variable.
    • Perl 1, 2, 3 and 4 didn't have lexical variables. Or name spaces. All variables where global. Was Perl evil then? I don't think so.
    • "Global variables bad; non-global variables good" is an often chanted mantra, but it's an incorrect one. What is considered good programming is to have variables with a scope as small as needed. And that when you can use a lexical variable instead of a package variable, the lexical is usually preferred. Note that this does not mean every global variable is bad - it's not a rare occasion having a global variable that is appropriate.
    • Whether you use a global variable or not usually doesn't have much impact on speed. And if it does have much impact, it's often caused by passing large variables into subroutines in such a way that copying happens; if that's the case, globals may be a winner (although using references often reduces the need for copying as well).
      "Perl 1, 2, 3 and 4 didn't have lexical variables. Or name spaces. All variables where global"
      Where did the local statement fit into this absolutist scheme?
        local() essentially replaces the current variable (package variable - or aggregate element) with another variable, until the runtime exits the scope the local() is in.

        If no magic is involved, it may be easier to think of it as temporary replacing the value.

Re: Are global variables "bad"?
by Argel (Prior) on Apr 21, 2009 at 21:25 UTC
    I really don't think using globals will come back to haunt you in "mysterious and underhanded ways". You can safely use them, even in large projects. It's just that often in large projects they are more cumbersome and problematic to deal with.

    In your case it really comes down to what you think this program will look like a few years from now. If you think it will be about the same size, etc. then I would continue with what you have. If you think there is a good chance it will turn into a much larger and much more complex program then avoiding globals would likely be a better choice. Also, if you do not have a lot of OO experience then you should consider how that will delay your project and how likely you will introduce different types of issues due to that lack of experience.

    I guess what I am trying to say is that you should avoid using globals for sound technical reasons instead of fear and cargo cult culture.

    Note: I assume by "global" you mean variables declared in the main:: scope (as opposed to e.g. "our" variables).

    Elda Taluta; Sarks Sark; Ark Arks

      Hmm. Ok, well this brings up another question. I guess I kind of assumed that "our" variables were just another way of declaring variables that could be used throughout the main program. Now that I think of it, I guess that makes no sense. Which, er might mean I'm using that incorrectly.

      If you declare a "local" variable with "my" in the main scope, you can still access it in all of the subroutines in the program. So, how is using "our" different than that? Is that similar to declaring something "public" in an object in java? i.e. if I were creating an object everyone could access (change?) an "our" declared variable, while a "my" declared variable ... not accessible? Or not changeable?

      Update: ok, little trip to our has revealed that I appear to have been completely wrong, except for the parts I was right about. Something which I'm still not clear about though is the accessibility of "our" vs. "my" declared variables. If one can "access" them does that imply the ability to change them? I'm thinking about this as an "our" variable being able to be accessed by something outside the main scope... is it just visible, or is it mutable? (are "my" declared variables visible outside of the scope they are called in? I'm assuming definitely not mutable).
        I think you might like the my() and our() thread. Lots of good links and discussion.

        Elda Taluta; Sarks Sark; Ark Arks

Re: Are global variables "bad"?
by BrowserUk (Pope) on Apr 22, 2009 at 00:17 UTC

    Saying "Global variables are bad!", is like saying "Drugs are bad!". Used wrongly they can be harmful. Used correctly they are a powerful good.

    And remember, entries in the symbol table are just global variables. So, if you substitute a 'singleton' instance, for a global variable, you are just substituting (at least) one global variable for another. And making your code far more complicated to boot.

    If you need global access to something, use a global variable. It's cleaner, clearer and safer.

    Like 'democracies' in which only one party is allowed, classes in which only one instance is allowed, are just a sham, putting a politically acceptable face on the truth.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      If you need global access to something, use a global variable. It's cleaner, clearer and safer.
      In fact, if anything, the OP could probably benefit more from Dominus's excellent and now freely available book Higher Order Perl. A lot of the concepts in there like dispatch tables, etc. are more likely to lead to a better program than a crash course on OO will.

      Elda Taluta; Sarks Sark; Ark Arks

        A lot of the concepts in there like dispatch tables, etc. are more likely to lead to a better program than a crash course on OO will.

        I couldn't agree more. (Though with a caveat!)

        Dominus covers some really useful techniques in HOP. And does so in a very clear and logical style. He is to be congratulated on the clarity of both his writing style and the way the topics covered, fit to together, and follow on from each other. I hope he made (is making) some money from it. (Directly or indirectly.)

        The caveat is to only use those technques where they fit your application's requirements. As with OO, don't try and force-fit your application to the techniques. The downfall of all programming methodologies is when they get adopted wholesale to the exclusion of all else. Some algorithms and requirements are a good fit for OO; other less so. Sometimes FP techniques are the way; other times not. Sometimes, good old-fashioned procedural techniques are all that is required and the clearest solution.

        The trick with all these things is to pick the right tool for the particular job, and avoid the 'I have a hammer' scenario.

        A secondary caveat of the HOP techniques is to bear in mind the cost of Perl 5 function calls. Whilst the HOP techniques work, and the descriptions are clearer than many other treatments of the material, they do tend to rely upon lots of nested functions calls, each of which that does very little--and that exacts a substantial penalty in Perl 5.

        I'd love to see HOP adapted to Perl 6/Parrot, once that pairing starts delivering on its potential. I think that would be something really special.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Are global variables "bad"?
by bradcathey (Prior) on Apr 22, 2009 at 01:43 UTC

    As a Web programmer, I usually using CGI::Application already, so I will stick data in a param for use around a script.

    $self->param('hashref' => \%hash);

    and later:

    my %hash = %{ $self->param('hashref') };

    Again, only if that is a viable option.

    —Brad
    "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
Re: Are global variables "bad"?
by hangon (Deacon) on Apr 22, 2009 at 06:15 UTC

    Global variables are not bad. People who use global variables badly are bad.

    Your queasy feeling may be in response to a conditioned knee-jerk negative reaction to global variables that seems to pervade this field. I don't know where it all started, but my first CS professor pounded the idea into our heads that globals are unequivocally bad. Experience taught a different lesson.

    Fortunately BrowserUK, JavaFan and others provide a voice of reason in this thread. Its not the globals themselves, but how and where you use them that can cause problems. Properly applied globals can be powerful and avoid unnecessary complexity. But like other aspects of programming, proper usage requires some degree of thought and discipline.

      You are definitely correct about this. My first CS prof did the exact same thing. Its compounded by the fact that I've used global structures at times when they were certainly *not* appropriate. In some ways I am a little bit grateful to have a healthy level of distrust of global variables, since its making me question initially if they are the right fit for the problem. And of course it started this thread with all these great responses by you and everyone else as you mentioned.

      Perhaps a better way to say it is not that globals are bad, but that globals are easy to use badly.

      I often find that I am suspicious when I run across global variables (and now Singletons) in code that is larger than a single script. I know (from much painful experience) that the globals are likely to be the focus of surprising behavior from changes due to unrelated code.

      Personally, I learned this not from any CS classes, but from being bitten a few times too many.

      G. Wade
Re: Are global variables "bad"?
by pileofrogs (Priest) on Apr 22, 2009 at 14:52 UTC

    I was recently trying to explain OOP to my dad, who's an old fortran programmer. One of the things I said was, "it's a way to have global variables without the danger."

    I wouldn't recommend making your object by hand the old fashioned way. I'd try one of the nifty packages that do a lot of the grunt work. The only one that pops into my head right now is Moose, but I know there are others.

    Re: singleton,

    { my $foo; sub set_foo { $foo = shift; } sub get_foo { return $foo; } }
    Is that a singleton? If so, I never knew the name.

    How would closures fit in this conversation? It seem like they'd be a middle-point between a singleton and an object.

    sub make_accessor { my $foo; return sub { my $in = shift; if defined($in) { $foo = $in; } return $foo; } }

    Thanks
    --Pileofrogs

Re: Are global variables "bad"?
by dwm042 (Priest) on Apr 22, 2009 at 19:37 UTC
    Some comments, as I was doing scientific programming of one kind or another for a number of years..

    1. Small scientific programs without fancy output generally don't need objects.

    2. Your program, however, is about 80% of its way to using an object.

    3. As long as you only wish to display 1 dataset you're fine as you are.

    4. Should you ever wish to get fancy with your display, such as embedding a different dataset as an inset view, then you can start encapsulating your data along with their associated screen transformation parameters or drive yourself crazy. And as ELISHEVA has pointed out, it's easier to manage input options with an OO framework.

    Update: Clarified some language.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://759043]
Approved by Corion
Front-paged by almut
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (8)
As of 2014-07-24 11:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (160 votes), past polls