Examples of the perils of globals in R and Stata Examples of the perils of globals in R and Stata r r

Examples of the perils of globals in R and Stata


I also have the pleasure of teaching R to undergraduate students who have no experience with programming. The problem I found was that most examples of when globals are bad, are rather simplistic and don't really get the point across.

Instead, I try to illustrate the principle of least astonishment. I use examples where it is tricky to figure out what was going on. Here are some examples:

  1. I ask the class to write down what they think the final value of i will be:

    i = 10for(i in 1:5)    i = i + 1i

    Some of the class guess correctly. Then I ask should you ever write code like this?

    In some sense i is a global variable that is being changed.

  2. What does the following piece of code return:

    x = 5:10x[x=1]

    The problem is what exactly do we mean by x

  3. Does the following function return a global or local variable:

     z = 0 f = function() {     if(runif(1) < 0.5)          z = 1     return(z)  }

    Answer: both. Again discuss why this is bad.


Oh, the wonderful smell of globals...

All of the answers in this post gave R examples, and the OP wanted some Stata examples, as well. So let me chime in with these.

Unlike R, Stata does take care of locality of its local macros (the ones that you create with local command), so the issue of "Is this this a global z or a local z that is being returned?" never comes up. (Gosh... how can you R guys write any code at all if locality is not enforced???) Stata has a different quirk, though, namely that a non-existent local or global macro is evaluated as an empty string, which may or may not be desirable.

I have seen globals used for several main reasons:

  1. Globals are often used as shortcuts for variable lists, as in

    sysuse auto, clearregress price $myvars

    I suspect that the main usage of such construct is for someone who switches between interactive typing and storing the code in a do-file as they try multiple specifications. Say they try regression with homoskedastic standard errors, heteroskedastic standard errors, and median regression:

    regress price mpg foreignregress price mpg foreign, robustqreg    price mpg foreign

    And then they run these regressions with another set of variables, then with yet another one, and finally they give up and set this up as a do-file myreg.do with

    regress price $myvarsregress price $myvars, robustqreg    price $myvarsexit

    to be accompanied with an appropriate setting of the global macro. So far so good; the snippet

    global myvars mpg foreigndo myreg

    produces the desirable results. Now let's say they email their famous do-file that claims to produce very good regression results to collaborators, and instruct them to type

    do myreg

    What will their collaborators see? In the best case, the mean and the median of mpg if they started a new instance of Stata (failed coupling: myreg.do did not really know you meant to run this with a non-empty variable list). But if the collaborators had something in the works, and too had a global myvars defined (name collision)... man, would that be a disaster.

  2. Globals are used for directory or file names, as in:

    use $mydir\data1, clear

    God only knows what will be loaded. In large projects, though, it does come handy. You would want to define global mydir somewhere in your master do-file, may be even as

    global mydir `c(pwd)'
  3. Globals can be used to store an unpredictable crap, like a whole command:

    capture $RunThis

    God only knows what will be executed. This is the worst case of implicit strong coupling, but since I am not even sure that RunThis will contain anything meaningful, I put a capture in front of it, and will be prepared to treat the non-zero return code _rc. (See, however, my example below.)

  4. Stata's own use of globals is for God settings, like the type I error probability/confidence level: the global $S_level is always defined (and you must be a total idiot to redefine this global, although of course it is technically doable). This is, however, mostly a legacy issue with code of version 5 and below (roughly), as the same information can be obtained from less fragile system constant:

    set level 90display $S_leveldisplay c(level)

Thankfully, globals are quite explicit in Stata, and hence are easy to debug and remove. In some of the above situations, and certainly in the first one, you'd want to pass parameters to do-files which are seen as the local `0' inside the do-file. Instead of using globals in the myreg.do file, I would probably code it as

    unab varlist : `0'    regress price `varlist'    regress price `varlist', robust    qreg    price `varlist'    exit

The unab thing will serve as an element of protection: if the input is not a legal varlist, the program will stop with an error message.

In the worst cases I've seen, the global was used only once after having been defined.

There are occasions when you do want to use globals, because otherwise you'd have to pass the bloody thing to every other do-file or a program. One example where I found the globals pretty much unavoidable was coding a maximum likelihood estimator where I did not know in advance how many equations and parameters I would have. Stata insists that the (user-supplied) likelihood evaluator will have specific equations. So I had to accumulate my equations in the globals, and then call my evaluator with the globals in the descriptions of the syntax that Stata would need to parse:

args lf $parameters

where lf was the objective function (the log-likelihood). I encountered this at least twice, in the normal mixture package (denormix) and confirmatory factor analysis package (confa); you can findit both of them, of course.


One R example of a global variable that divides opinion is the stringsAsFactors issue on reading data into R or creating a data frame.

set.seed(1)str(data.frame(A = sample(LETTERS, 100, replace = TRUE),               DATES = as.character(seq(Sys.Date(), length = 100, by = "days"))))options("stringsAsFactors" = FALSE)set.seed(1)str(data.frame(A = sample(LETTERS, 100, replace = TRUE),               DATES = as.character(seq(Sys.Date(), length = 100, by = "days"))))options("stringsAsFactors" = TRUE) ## reset

This can't really be corrected because of the way options are implemented in R - anything could change them without you knowing it and thus the same chunk of code is not guaranteed to return exactly the same object. John Chambers bemoans this feature in his recent book.