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

Best practices with globals and subroutine arguments

by Foxpond Hollow (Sexton)
on Jun 07, 2010 at 02:47 UTC ( [id://843390]=perlquestion: print w/replies, xml ) Need Help??

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

Monks, I come to you today with more of a philosophical question. I'm writing a script with about six highly interrelated subroutines -- at various points they all pass or get the same ten or so variables to or from each other. Most of these variables are hashes or arrays, so there's a lot of referencing and dereferencing going on as well. And in almost all cases, none of the subroutines are actually modifying these variables once they get them, just using them to calculate other things. My understanding of best practices is that everything should be contained to avoid any accidental contamination, and global variables should be used as little as possible. But in this case, a bunch of global variables would probably simplify things considerably. Is it still advisable to pass the variables and keep things local, or is it sometimes acceptable to keep a bank of globals?
  • Comment on Best practices with globals and subroutine arguments

Replies are listed 'Best First'.
Re: Best practices with globals and subroutine arguments
by BrowserUk (Patriarch) on Jun 07, 2010 at 03:54 UTC

    In general, the need for such is usually either a) an indication of too general a data structure; too much in one pot; or b) poorly factored code; doing too many only vaguely related things in each subroutine.

    There are occasions--for example; configuration parameters--where it makes some sense to put lots of little things into one encompassing structure. And purely on the basis of your description, this might be one of them. But if all 6 subroutines need access to all 10 variables or structures, why not just wrap them into one encompassing (lexical) structure and pass a reference to that? I won't claim a "best practice" title for this, but it would be easier and probably safer.

    Another possibility for avoiding the globals is to make them methods of an "object". You don't even have to go the full OO hog. You can achieve most of the same benefits by using a closures:

    { my( $var1, %var2, @var3, $var4 ... ); sub initVars{ $var1 = ...; %var2 = ...; } sub depSub1 { while( my $k, $v = each %var2 ) { } } sub depSub2 { for my $elem ( @var3 ) { } } }

    The above is simple to write and requires virtually no changes to the calling code, but achieves most of what an explicit OO module would achieve. Plus, calling globals "closures" seems to take the sting out of most arguments against them :)

    Of course, sticking the keyword package inside the outer set of curlies and having init() return a blessed reference isn't much harder either. But it does require more changes in the calling code.


    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: Best practices with globals and subroutine arguments
by chromatic (Archbishop) on Jun 07, 2010 at 06:51 UTC

    Your problem isn't that using globals is dangerous. Your problem is that the responsibility for many separate things is all over your program.

    Extract separate behaviors and encapsulate separate responsibilities and you'll discover that the need for passing around lots of arguments and referencing and dereferencing lots of values will go away.

Re: Best practices with globals and subroutine arguments
by desemondo (Hermit) on Jun 07, 2010 at 03:40 UTC
    Your question is highly open to interpretation based on the situation/problem to be solved.

    Personally I would still avoid using our variables, but in some situations I wouldn't hesitate to use a handful of my variables directly underneath use strict; use warnings;

    Also, if these global variables are constant for the duration of the script's running lifetime, then you might consider using use constant or use Readonly::XS

    Or, if you have complete control over this code, restricted hashes might be a suitable fit. Essentially you would declare a hash in your most outer scope, then using the Hash::Util module's lock_hash() and unlock_hash() subroutines whenever you need to modify or "make constant" your global variables...

    Disclaimer: This is just my opinion, and others may disagree with these approaches.
      It really depends on the scope of the project. If it's a big project spread across multiple files with more than one developer working on it then I would avoid global. On the other hand, if it's a small to medium project in one (or maybe two) files, with only you working on it then I wouldn't hesitate to put some "my" variable below your "use" statements (i.e. variables in scope main::). Caveat: I mostly write sysadmin type scripts these days.

      Elda Taluta; Sarks Sark; Ark Arks

Re: Best practices with globals and subroutine arguments
by proceng (Scribe) on Jun 07, 2010 at 03:30 UTC
    And in almost all cases, none of the subroutines are actually modifying these variables once they get them, just using them to calculate other things.
    I guarantee that, at some point, that one word will come back to bite you. Either you (or someone else will modify one of the subroutines and you will find out the true meaning of "a house of cards".
Re: Best practices with globals and subroutine arguments
by JavaFan (Canon) on Jun 07, 2010 at 07:13 UTC
    I'd probably use "global" variables.

    I wouldn't say on Perlmonks I'd be using "global variables". Instead, I'd label those variables class instance variables, and call the class they're defined in a "singleton object". Then non of the "global variables are bad" triggers will fire; instead, people will think the world of you. But except for the labels, there's no difference.

      I'd probably use "global" variables. I wouldn't say on Perlmonks ...

      JavaFan, today you have

      Experience:  	11072
      Level:	Prior (17)
      Writeups: 	2074 
      You obviously know about scoping, about the best practices and when its ok to break them; you're an expert.

      How often do you use globals to avoid de-referencing references, and does that simplify things considerably?

        The OP indicates the variables are not modified inside the subroutines. For the POV of the subroutine, the variables might as well be constants.

        How often do you use "global" constants, and does that simplify your code instead of passing them around in each subroutine call?

        You obviously know about scoping, about the best practices and when its ok to break them; you're an expert.
        I find this a bit insulting for the OP. It suggests the OP is an idiot, or at least needs to program with his training wheels on. I do not with to make such assumptions.
Re: Best practices with globals and subroutine arguments
by Herkum (Parson) on Jun 07, 2010 at 14:34 UTC

    I rarely, if ever use global variables, they don't really provide many benefits over objects or local variables and can potentially lead to many bugs.

    If I were you, I would stick all my data into a readonly data object. Then you can pass your subroutines the data object instead. This way you can make your data specifically 'READONLY', ie: no gotchas which your subroutines "accidently" change a value.

Re: Best practices with globals and subroutine arguments
by Anonymous Monk on Jun 07, 2010 at 03:04 UTC
    But in this case, a bunch of global variables would probably simplify things considerably.

    Doubtful

Re: Best practices with globals and subroutine arguments
by gnosti (Chaplain) on Jun 09, 2010 at 09:19 UTC
    I've been following the general disparagement of using global variables on PM for at least a year.

    I am especially curious when I read comments (as above) to the effect that global variables are unconditionally poor practice.

    Global variables have a respectable place in the annals of computer science. In recent history, the linux kernel is a large project that uses global variables. Linus originally elected this design to avoid the message-passing overhead of a microkernel. That was a technical decision, and kernel programmers seem to be bearing the maintenance burden fairly well. So are the maintainers of billions of lines of COBOL, also no doubt using global variables.

    Is the vociferous advocacy against global variables due to module authors' focus on separation of concerns, due to devotion to achieving reliability in large systems or what?

    My application, Audio::Nama started with a couple hundred lines of code. Now it has grown to having more than 200 global variables in the package's root namespace.

    Some 25 of these variables house widgets related to the Tk GUI. I could take them out of the root namespace and put them in a GUI namespace, but they'll still be globals.

    What benefit will I have from rewriting every statement referring to one of these variables?

    Configuration and state variables in the global namespace are convenient. The app has a shell that lets me dump variables and data structures at will. I have a useful test suite that helps me track regressions, and use version control to develop new functions. Does the convenience of globals and the inconvenience of other solutions figure into the 'globals are bad practice' calculation?

    Things have broken for many many reasons over the five year life of my project. Mistakenly assigning to a global variable may have happened only a couple times, typically when using '=' instead of '==' in a comparison.

    Some could criticize my app as being a ball of mud. Certainly high level structures have come gradually. I've only introduced namespaces and classes when confronted with difficulties in reading or maintaining the code that I couldn't otherwise resolve.

    Global variables is an area I'm considering, however I am still looking for alternatives and a motivation to shift over to other constructs from this straightforward way of addressing data.

    Namespaces became practical for me to introduce when I discovered that I could use 'our' declarations in a single file. That allowed me to move code to other namespaces while still accessing variables in the root namespace.

    Although I want to refactor my code to be more reliable and easier to maintain, any steps will need to be sufficiently incremental. Over time I've found attempts at search-and-replace on variable names to be suprisingly error prone.

      Would it be useful then perhaps, to have the ablity to mark a variable as readonly if current scope is below the scope in which a variable was declared?

      ie. Variable can only be modified within the scope it was declared and not somewhere below?

      use strict; use warnings; my $g1; my $g2 :resticted ; #or some other attribute mechanism $g1 = 'boo'; $g2 = 'blah'; { $g1 = 'changed' ; #OK. Same behaviour as now $g2 = 'cant change'; #Can't do this, ideally throw an exceptio +n my $copy_of_g2 = $g2; #OK. I can still read $g2. } print "$g1\n" print "$g2\n" __END__ changed blah
      I have no idea if this is possible (which I actually consider secondary), I'm asking if anyone else thinks this type of functionality would be useful to have. Or if this behaviour is already available which module(s) provide it.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (5)
As of 2024-04-18 02:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found