Hi All,
I'm playing around with DOSUBL. Trying to understand how macro variables created (or modified) in the DOSUBL environment are returned to the calling environment. (tested in 9.3M2 and 9.4M0 on Win7 PC SAS).
Hit one oddity that looks like a bug to me.
Conditions:
Result
Here is sample code and log:
1 %macro TestDoSubl(x=); 2 %local rc; 3 4 %let rc=%sysfunc(dosubl(%nrstr( 5 %let x=dosubl; 6 %put in DOSUBL environment; 7 %put _user_; 8 %put; %put; %put; 9 ))); 10 11 %put after DOSUBL; 12 %put _user_; 13 %put; %put; %put; 14 15 %mend TestDoSubl; 16 17 %let x=global; in DOSUBL environment GLOBAL X dosubl GLOBAL RC TESTDOSUBL X local <--- The X in the local symbol table has value local, as expected TESTDOSUBL RC GLOBAL X global 18 %TestDoSubl(x=local) after DOSUBL TESTDOSUBL X global <--- After DOSUBL completed, X in the local table has value global. I would expect it to have value dosubl. TESTDOSUBL RC 0 GLOBAL X global 19 20 %put after macro; after macro 21 %put _user_; GLOBAL X global
Observations:
In the DOSUBL, environment, the local macro variables that exist in main session are imported as global macro variables (fair). Note that the %PUT statement in the DOSUBL environment shows two different global macro variables named X. (This can also be seen when FCMP/run_macro is used to run code on the side).
After the DOSUBL has completed, I would expect it to return the global macro variable &x=dosubl back to the local symbol table. But it looks to me like DOSUBL gets confused, and instead is returning the other global macro variable &x=global back to the local symbol table.
Thoughts whether this is a bug, or if I'm not understanding the expected results?
So far the only other oddity I noticed is that if in the main environment, &x exists in the global symbol table but not the local symbol table, DOSUBL will update the global macro variable (fair) and also create a local macro variable with the same value (odd, but not much of a concern).
Thanks
Ack, seems my idea of using %NRSTR() may be part of the problem? I'm not sure how it effects the timing. I did notice that order of the log looks odd (results of first %put statement before the macro invocation). Will keep playing.
The function dosubl is called from within a running macro. In my vision that is a perfect reason to see all new macro vars as defined local.
As you have used x as a amco-function parameter from that point it is local SAS(R) 9.4 Macro Language: Reference, Third Edition not touching the global one anymore.
Hi Jaap,
I agree, that new macro vars should be local. And I am happy that the end result here is that the global macro variable X is not touched.
The surprise to me (and suspect bug), is after DOSUBL has completed, instead of it writing the value it computed for X to the local symbol table, somehow the value from the global symbol table is written to the local symbol table. That's the surprise. I will add a comment to the log to in my post to try to make this more clear.
Quentin you coded the macro var x also at: %macro TestDoSubl(x=);
At that moment it is becoming local defined by x=....
Yes, there is a local macro variable x that I passed in as a parameter, and that is the correct behavior.
There is also a global macro variable x that I created with a %LET statement in open code, and that is the correct behavior.
The problem is that the value of the local macro variable x becomes over written by the value of the global macro variable x after the DOSUBL has completed.
I see the following:
%let x=global; (x as global defined)
Anything referring to x will use the global definition.
%TestDoSubl(x=); (call/start Will active level1 of macro-processing start dedicated variables scope)
Anything referring to x will use this local definition.
Macro TestDoSubl will proceed to execute.
> %let x=dosubl; (from macro) will process x as local as the macro TestDoSubl is active (level1)
%TestDoSubl(x=); (call/end Will end level1 of macro processing removing variables belonging to this scope )
Only the original global macro-vars still there.
You can build up a nesting sequence of local-vars (explicitly local defines) or having automatic inheritance of the local to a caller.
It is a nesting scope of varaibles, SAS(R) 9.4 Macro Language: Reference, Third Edition.
Not being explained well in older doc's or very difficult to get when you are trying to learn by example. Seen more programming languages you could deduce this.
The same variable-name is intended to have different contenst depending on its scope.
Thanks for not giving up on the discussion, Jaap. But I don't think your seeing my point.
Yes, I understand the scoping rules of the macro language, and they are as you described them. Here is a shorter example of some test code to show the surprising result.:
%macro testNoDosubl(x=); %put x=&x; %mend testNoDosubl; %macro testWithDosubl(x=); %let rc=%sysfunc(dosubl(%nrstr(%let x=3;))); %put x=&x; %mend testWithDosubl; %let x=1; %testNoDosubl(x=2) %testWithDosubl(x=2)
When %testNoDosubl runs, what is the expected result shown in the log? It is 2. Because the %put statement returns the value of x from the local symbol table, not the global symbol table. That is the usual macro scoping rule. No surprise.
When %testWithDoSubl runs, what is the expected result? I'm not sure. But I would hope 3 (if the %let statement in the DoSubl block updated the local symbol table) or 2 (if it did not). But in fact it returns 1. What can explain why %testWithDoSubl returns 1?
My explanation would be that DoSubl is returning the wrong value to the local symbol table. If you add %put _global_; to the DoSubl block after the %let statement, it will show that in the 'side-session' where DoSubl runs, it can see two global macro variables named x. It sees the global macro variable x in the main session (with value 1), and the global macro variable x in the side-session (with value 3). I think DoSubl is intended to return the value from the side-session to the local symbol, but accidentally returns the value from the main session to the local symbol table.
Do you see another explanation for why %testWithDoSubl returns 1?
I see your issue now, it is not very clear to see. There are some new options that are very helpful with debugging.
During the scope of the macro the local var x get the value of the global version. That must be a bug.
42 ;
43 options mprint mprintnest mlogic mlogicnest symbolgen;
44
45 %macro testNoDosubl(x=);
46 %put x=&x %symlocal(x);
47 %mend testNoDosubl;
48
49 %macro testWithDosubl(x=);
50 %local rc;
51 %put x=&x %symlocal(x);
52 %let rc=%sysfunc(dosubl(%nrstr(%let x=3;)));
53 %put x=&x %symlocal(x);
54 %put _user_;
55 %mend testWithDosubl;
56
57 %macro testNESTsubl(x=);
58 %local rc;
59 %put x=&x %symlocal(x);
60 %let rc=%sysfunc(dosubl(%nrstr(%testWithDosubl(x=5)
61 )));
62 %put x=&x %symlocal(x);
63 %put _user_;
64 %mend testNESTsubl;
65
66
67 %let x=1;
68 %put x=&x %symlocal(x);
SYMBOLGEN: Macro variable X resolves to 1
x=1 0
69 %put _user_;
GLOBAL GRAPHINIT
GLOBAL GRAPHTERM
GLOBAL X 1
GLOBAL _CLIENTAPP SAS Studio
GLOBAL _CLIENTAPPVERSION 3.1
GLOBAL _SASPROGRAMFILE folders myshortcuts ikke macroscope_001.sas
GLOBAL _SASWSTEMP_ folders myfolders .images f9d75edd 6a9c 4b8e b177 abbba13bbe38
GLOBAL _SASWS_ folders myfolders
70 %testNESTsubl(x=4)
MLOGIC(TESTNESTSUBL): Beginning execution.
MLOGIC(TESTNESTSUBL): Parameter X has value 4
MLOGIC(TESTNESTSUBL): %LOCAL RC
MLOGIC(TESTNESTSUBL): %PUT x=&x %symlocal(x)
SYMBOLGEN: Macro variable X resolves to 4
x=4 1
MLOGIC(TESTWITHDOSUBL): Beginning execution.
MLOGIC(TESTWITHDOSUBL): Parameter X has value 5
MLOGIC(TESTWITHDOSUBL): %LOCAL RC
MLOGIC(TESTWITHDOSUBL): %PUT x=&x %symlocal(x)
SYMBOLGEN: Macro variable X resolves to 5
x=5 1
MLOGIC(TESTWITHDOSUBL): %LET (variable name is RC)
MLOGIC(TESTWITHDOSUBL): %PUT x=&x %symlocal(x)
SYMBOLGEN: Macro variable X resolves to 1
x=1 1
MLOGIC(TESTWITHDOSUBL): %PUT _user_
TESTWITHDOSUBL GRAPHINIT
TESTWITHDOSUBL GRAPHTERM
TESTWITHDOSUBL RC 0
TESTWITHDOSUBL X 1
TESTWITHDOSUBL _CLIENTAPP SAS Studio
TESTWITHDOSUBL _CLIENTAPPVERSION 3.1
TESTWITHDOSUBL _SASPROGRAMFILE folders myshortcuts ikke macroscope_001.sas
TESTWITHDOSUBL _SASWSTEMP_ folders myfolders .images f9d75edd 6a9c 4b8e b177 abbba13bbe38
TESTWITHDOSUBL _SASWS_ folders myfolders
GLOBAL RC
GLOBAL X 4
TESTNESTSUBL RC
TESTNESTSUBL X 4
GLOBAL GRAPHINIT
GLOBAL GRAPHTERM
GLOBAL X 1
GLOBAL _CLIENTAPP SAS Studio
GLOBAL _CLIENTAPPVERSION 3.1
GLOBAL _SASPROGRAMFILE folders myshortcuts ikke macroscope_001.sas
GLOBAL _SASWSTEMP_ folders myfolders .images f9d75edd 6a9c 4b8e b177 abbba13bbe38
GLOBAL _SASWS_ folders myfolders
MLOGIC(TESTWITHDOSUBL): Ending execution.
MLOGIC(TESTNESTSUBL): %LET (variable name is RC)
MLOGIC(TESTNESTSUBL): %PUT x=&x %symlocal(x)
SYMBOLGEN: Macro variable X resolves to 1
x=1 1
MLOGIC(TESTNESTSUBL): %PUT _user_
TESTNESTSUBL RC 0
TESTNESTSUBL X 1
GLOBAL GRAPHINIT
GLOBAL GRAPHTERM
GLOBAL X 1
GLOBAL _CLIENTAPP SAS Studio
GLOBAL _CLIENTAPPVERSION 3.1
GLOBAL _SASPROGRAMFILE folders myshortcuts ikke macroscope_001.sas
GLOBAL _SASWSTEMP_ folders myfolders .images f9d75edd 6a9c 4b8e b177 abbba13bbe38
GLOBAL _SASWS_ folders myfolders
MLOGIC(TESTNESTSUBL): Ending execution.
71
72 %put x=&x %symlocal(x);
SYMBOLGEN: Macro variable X resolves to 1
x=1 0
73 %put _user_;
GLOBAL GRAPHINIT
GLOBAL GRAPHTERM
GLOBAL X 1
GLOBAL _CLIENTAPP SAS Studio
GLOBAL _CLIENTAPPVERSION 3.1
GLOBAL _SASPROGRAMFILE folders myshortcuts ikke macroscope_001.sas
GLOBAL _SASWSTEMP_ folders myfolders .images f9d75edd 6a9c 4b8e b177 abbba13bbe38
GLOBAL _SASWS_ folders myfolders
74
75 ;
Thanks again Jaap. Glad you agree. I think I'll try posting this to SAS-L as well, to see if I can prompt more discussion there. Then will send it in to tech support and/or Rick Langston directly (pretty sure DOSUBL is his creation). I really like his last example of using DOSUBL for function-style macros (https://support.sas.com/resources/papers/proceedings13/032-2013.pdf), so feel like it's worth time to test out some of the magic involved in moving things back and forth between the main session and the side-session.
I'm not sure. After posting this I followed up with Rick Langston, and he agreed it was a bug and said he was working on a hot-fix. I'm not sure if it was going to be an official hot-fix or if it would perhaps be slipped into a maintenance release.
Perhaps folks who have a fully hot-fixed version of 9.4M3 could try the example I posted and let us know?
I just ran it in AWS SAS Studio (9.04.01M3P06242015), and got the same scope collision I reported above. I'm not sure how hot-fixed the AWS version is.
@Quentin I contacted SAS Support on the Status of this Issue and they responded saying this is a difficult issue to fix via a HOTFIX in SAS 9.4M3 and therefore will be planned for a future release of SAS. No timetable has been set for this however.
Also i have heard from one of my Colleauges at Work that SAS may be coming out with Release 10 next year instead of 9.4M4 or 9.5. Not sure how much of this is True.Need to wait and See.
Thanks @pchegoor sorry to hear there is no timetable, but good to know it's still on their list of things to work on. I think there be magic in this "side-session" processing, but until I have a better understanding of how the main environment and side environment communicate with each and inherit from each other, I don't acticipate using it for production code. But certainly the promise/potential of DOSUBL is very exciting.
Good to hear the rumor about SAS 10. Hope for 10 will be more motivation to hit Vegas for SGF2016.
@Quentin I am happy to Report that the DOSUBL Macro variable Scope Issue has been fixed in SAS 9.4M4. I have run the below Codes on SAS University Edition with 9.4M4.
%let a = 1;
%let b = 2;
%let c = 3;
%macro test2;
%local a b c;
%let a = 10;
%let b = 20;
%let c = 30;
%put BEFORE DOSUBL: a is &a, b is &b, c is &c;
%let rc = %sysfunc(dosubl(%str(data _null_;run; )));
%put AFTER DOSUBL: a is &a, b is &b, c is &c;
%mend test2;
%test2
Result :
%macro testNoDosubl(x=);
%put x=&x;
%mend testNoDosubl;
%macro testWithDosubl(x=);
%let rc=%sysfunc(dosubl(%nrstr(%let x=3; )));
%put x=&x;
%mend testWithDosubl;
%let x=1;
%testNoDosubl(x=2)
%testWithDosubl(x=2)
%macro TestDoSubl(x=);
%local rc;
%let rc=%sysfunc(dosubl(%nrstr(
%let x=dosubl;
%put in DOSUBL environment;
%put _user_;
%put; %put; %put;
)));
%put after DOSUBL;
%put _user_;
%put; %put; %put;
%mend TestDoSubl;
%let x=global;
%TestDoSubl(x=local)
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.