SAS macro variable scope

I’m going to assert something fairly uncontroversial: it’s good practice to limit the scope of your variables. The reasoning behind this is fairly easy to follow: the more places a variable’s value can be altered, the better the chance that at some point it will be altered unintentionally.

This is particularly true in the SAS macro language, where the rules governing how variables are resolved are fairly simple, but where it’s easy to lose sight of how conflicting scope can pose a risk. For a slightly contrived example, in the macro %test below, the value of the global variable x is checked. In %set, the value of x is changed:

%let x=Y;

%macro test;
  %if &x = Y %then %put "Flag set";
  %else %put "Flag not set";
%mend test;

%macro set(val);
  %let x=&val;
%mend set;

/*Some code*/
/*Some other code*/

This all looks fine, and it is – until you call the macro %pointless_loop between %set and %test:

%macro pointless_loop(num_mths);
%let start=%sysfunc(intnx(month, %sysfunc(date()), -&num_mths, e));
%do i=0 %to &num_mths;
    %let x=%sysfunc(intnx(month, &start, &i, b), date9.);
%mend pointless_loop;

The problem here is that, by the time this macro is executed, x already exists in the global symbol table. If this wasn’t the case, x would be created in the symbol table local to %pointless_loop, and would be deleted with that symbol table when it finished executing. As it is, when %test runs, the x in the global symbol table will be altered, and will hold a formatted date string rather than the Y or N it’s intended to contain. This type of situation can arise very easily – particularly if macros are being re-used across applications. Ideally, you shouldn’t need to care what names are assigned to variables defined inside them.

How could this be avoided? Well, apart from resisting the temptation to use variable names like x (as a rule, I think variable names should be as descriptive as possible), be explicit. There doesn’t need to be a problem with having x in both the global and local symbol table – just describe the scope before using them:

%global x;
%let x=Y;

%macro pointless_loop(num_mths);
%let start=%sysfunc(intnx(month, %sysfunc(date()), -&num_mths, e));
%local x;
%do i=0 %to &num_mths;
    %let x=%sysfunc(intnx(month, &start, &i, b), date9.);
%mend pointless_loop;

Now the value of x in the global symbol table will be left untouched.

2 thoughts on “SAS macro variable scope

  1. Evan says:

    The only thing better than “x” as a variable name is “i”! As in:

    10 FOR I = 1 TO 100
    30 GOTO 10

    Seriously though, I’m with you 100%. There’s a lot of benefits to programming in a flexible language like SAS, but it does put a lot more responsibility on the programmer to get things like scoping right. No different to any TIMTOWTDI language (like Perl), but it’s easy for people without a strong programming background to run into issues.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s