I found this very helpful SO page while trying to resolve an issue related to macro variable scope. why doesn't %let create a local macro variable?
So to summarize, writing %let x = [];
or %do x = [] %to [];
in a macro will:
This strikes me as very non-intuitive. I would be willing to bet there are tons of bugs out in the SAS wilderness due to this design choice. I rarely see %local statements in macros, even above loop statements using common variable names like "i" or "counter." For example, I just pulled up the first paper with the word "macro" in the title from this list of SUGI and SAS Global Forum papers http://www.lexjansen.com/cgi-bin/xsl_transform.php?x=sgf2015&c=sugi
And indeed, I found this code in the first SAS conference paper I opened:
%macro flag;
data CLAIMS;
set CLAIMS;
%do j= 1 %to 3;
if icd9px&j in (&codelist)
then _prostate=1;
%end;
run;
%mend;
%flag;
http://support.sas.com/resources/papers/proceedings15/1340-2015.pdf
Woe unto anyone who calls %flag and also has their own &j variable. They could easily end up with no log errors but bogus results because their &j is 4 everywhere after they call %flag, which will be (from experience) a bug that is no fun to track down. Or worse, they may never recognize their results are bogus.
So my question is, why was the decision made not to have all macro variables be local scope by default? Are there good reasons why SAS macro variable scope works the way it does?
Largely, because SAS is a 50 year old language which existed before lexical scoping was clearly preferred.
SAS has a mixture of the two scoping concepts, but is mostly dynamically scoped unless you intentionally change it. This means that just by reading a function's definition, you can't tell what variables will be available to it at run-time; and assignment statements apply to the version of a variable which is currently available at run-time (Rather than being enforced to be in the most local scope available).
That means that the macro compiler can't tell if a particular assignment statement is intended to be assigning a local macro variable, or a possibly-existing-at-runtime higher scope macro variable. SAS could enforce the local macro variable as you state, but that would turn SAS into a lexical scoping language, which isn't desired both based on consistency with past (keeping backwards compatibility) and based on functionality; SAS offers the ability to enforce lexical scoping (use %local
) but doesn't offer the ability to intentionally alter a variable in a higher scope (some form of parent
?) other than %global
.
Note that Dynamic Scoping was very common back in the 60s and 70s. S-Plus, Lisp, etc. all had dynamic scoping. SAS tends to prefer backwards compatibility as far back as possible. SAS also is commonly used analysts, rather than programmers, and so needs to avoid complexity whenever possible. They offer %local
for those of us who do want the advantages of lexical scoping
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With