0

The goal

I have a macro called localOrGlobal that takes input and creates output. The output is printed to the log during the macro call. However, I want the user to be able to specify if the output should be made available as a global variable by using the parameter globalVar = 0.

The problem

Unfortunately, I cannot manage to make output available outside of the scope of the localOrGlobal call.

The code

/*Data for example purpose*/
data a;
    column = "exampleText";
run;

/*The macro. Set 'globalVar = 1' to make the 'output' available for later use.*/
%macro localOrGlobal(input, globalVar = 0);

    /* Control flow to define output as a local or global variable */
    %if globalVar = 0 %then %do;
            %local output;
        %end;
    %else %if globalVar = 1 %then %do;
            %global output;
        %end;

    /* Step that creates the output from the input*/
    proc sql;
        select * into : output separated by ' '
        from &input.;
    quit;

    /* Prints output to log, proving that output was created in the macro*/
    %put &output.;

%mend localOrGlobal;

/*Testing macro with output as a local variable*/
%localOrGlobal(a);

/*Expect this to fail since output should not exist outside of scope of the localOrGlobal call*/
%put &output.;

/*Testing macro with output as a global variable*/
%localOrGlobal(a, globalVar = 1);

/*Expect this to succeed since output be a global variable and exist outside the scope of the localOrGlobal call*/
%put &output.;

The error message

61         /*Expect this to succeed since output be a global variable and exist outside the scope of the localOrGlobal call*/
62         %put &output.;
WARNING: Apparent symbolic reference OUTPUT not resolved.
&output.

The expanded log

REDACTED LINES 1-23

24         /*Data for example purpose*/
25         data a;
26             column = "exampleText";
27         run;

NOTE: The data set WORK.A has 1 observations and 1 variables.
NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.01 seconds
      

28         
29         /*The macro. Set 'globalVar = 1' to make the 'output' available for later use.*/
30         %macro localOrGlobal(input, globalVar = 0);
31         
32             /* Control flow to define output as a local or global variable */
33             %if globalVar = 0 %then %do;
34                     %local output;
35                 %end;
36             %else %if globalVar = 1 %then %do;
37                     %global output;
38                 %end;
39         
40             /* Step that creates the output from the input*/
41             proc sql;
42                 select * into : output separated by ' '
43                 from &input.;
44             quit;
45         
46             /* Prints output to log, proving that output was created in the macro*/
47             %put NOTE: your output is below.;
48             %put &output.;
49         
50         %mend localOrGlobal;
2                                                          The SAS System                             17:43 Friday, October 23, 2020

51         
52         /*Testing macro with output as a local variable*/
53         %localOrGlobal(a);
MPRINT(LOCALORGLOBAL):   proc sql;
MPRINT(LOCALORGLOBAL):   select * into : output separated by ' ' from a;
MPRINT(LOCALORGLOBAL):   quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.02 seconds
      cpu time            0.01 seconds
      

NOTE: your output is below.
exampleText
54         
55         /*Expect this to fail since output should not exist outside of scope of the localOrGlobal call*/
56         %put &output.;
WARNING: Apparent symbolic reference OUTPUT not resolved.
&output.
57         
58         /*Testing macro with output as a global variable*/
59         %localOrGlobal(a, globalVar = 1);
MPRINT(LOCALORGLOBAL):   proc sql;
MPRINT(LOCALORGLOBAL):   select * into : output separated by ' ' from a;
MPRINT(LOCALORGLOBAL):   quit;
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds
      

NOTE: your output is below.
exampleText
60         
61         /*Expect this to succeed since output be a global variable and exist outside the scope of the localOrGlobal call*/
62         %put &output.;
WARNING: Apparent symbolic reference OUTPUT not resolved.
&output.
63         
64         GOPTIONS NOACCESSIBLE;
65         %LET _CLIENTTASKLABEL=;
66         %LET _CLIENTPROCESSFLOWNAME=;
67         %LET _CLIENTPROJECTPATH=;
68         %LET _CLIENTPROJECTNAME=;
69         %LET _SASPROGRAMFILE=;
70         
71         ;*';*";*/;quit;run;
72         ODS _ALL_ CLOSE;
73         
74         
75         QUIT; RUN;
76         

Expected solution

I will mark a solution as correct if it provides modifications to my code that prints output to the log when calling %put &output.; in 'open code' after running %localOrGlobal(a, globalVar = 1);.

Pᴇʜ
  • 56,719
  • 10
  • 49
  • 73
Jayden.Cameron
  • 499
  • 4
  • 17

2 Answers2

1

You need to evaluate the value of a macro parameter by & resolving it.

%if &globalVar = 0 %then %do;

For logging macro variable values I recommend

%put NOTE: &=output;
Richard
  • 25,390
  • 3
  • 25
  • 38
  • Just a word of caution, there are very few use cases where a macro symbol in a macro definition would be either local or global depending on the caller, and things can get even trickier if you pass in the name of the macro symbol that may be local or global (i.e. replace your `output` symbol with `&mvarname` and add `mvnarme=output` to the macro definition parameter list. – Richard Oct 24 '20 at 14:19
  • If the macro is to 'return' or 'compute' a multi valued result for an outer scope consider biting the 'results will always be via global symbols' bullet, or, mayhap better, make results available via a data set (sometimes involving `%sysfunc(dosubl(` ). – Richard Oct 24 '20 at 14:19
1

You could fix your program without fixing the macro by just defining the macro variable before calling the macro.

 %let output=before macro call; 
 %localOrGlobal(a, globalVar = 1);
 %put &=output;

This is because the literal text globalVar is never going to equal the text 0 nor the text 1. So neither the %local nor the %global statement will run. You need to use &globalVar to use the vlaue of globalVar.

Your current logic, if it worked, treats GLOBALVAR as tri-level. 0 means make it local, 1 means make it global and anything else means follow SAS's normal scoping rules. But if you call this macro from another macro that has already defined output as a local macro variable then issuing a %GLOBAL statement for output will cause an error. You can fix that by testing if output is already defined.

Try logic like this instead.

%if &globalVar = 0 %then %local output;
%else %if (&globalVar = 1) and (not %symexist(output)) %then %global output;
Tom
  • 47,574
  • 2
  • 16
  • 29