DATA Step, Macro, Functions and more

Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Reply
Frequent Contributor
Posts: 133

Attempt to %GLOBAL a name (NAME) which exists in a local environment.

what following macro says the error? I want NAME to be global macro variable, how do I fix

ERROR: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

%macro t(name=);

%global name;

data &name;

x=1;

run;

%mend;

%t(name=p);

Super User
Super User
Posts: 6,495

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

You have already defined NAME as %local by defining it as a parameter.

If NAME is going to be the parameter then you would never want to define it as GLOBAL.

And your code does not have any need for a GLOBAL macro variable as it is not making any attempt to change the value of NAME and make it available after the macro ends.

Frequent Contributor
Posts: 133

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Then, how do I pass in a macro variable as global macro variable when calling than macro?

Super User
Super User
Posts: 6,495

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Not sure what you mean.  If you have a value you want to pass just use it in the macro call.

For example the code below will create a data set named WORK.NEWFILE .

%* Define the macro ;

%macro t(name=);

data &name;

  x=1;

run;

%mend;


%* Create a macro variable with name you want to use in the macro ;

%let name=work.newfile ;


%* Call the macro passing in the value of your existing macro variable. ;

%t(name=&name);

Super Contributor
Posts: 259

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Passing global variables to a macro is not necessary. If you want to create a global variable in a macro named by the value of a parameter, use

%macro t(name=);

     %global &name;

     ...

%mend;

Super Contributor
Posts: 339

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

You do not "pass a macro variable to a macro", you pass a string.

When you call %t(<text>) or %t(name=<text>), the text is stored in a temporary (local) macro variable name as by how you defined your macro.

If you want to emulate "passing" a global macro variable to a macro, you simply declare the macro variable outside (either in an upper nesting level for multiple macro nesting, or simply outside the macro a code with a single macro). You cannot pass a macro variable by reference, at least not conceptually since name= reads a text string into a new local macro variable called name.

/* First example - emulating passing a macro variable by reference - that is, being able to modify it in the macro */

%let name=work.myds; /* implicit %global name; */

/* some sas statements */

%macro t(); /* no name= in the macro definition as you wish NOT to create a local macro variable name so as not to lose the ability to work with the global macro variable name */

data &name;

x=1;

%let name=test;

run;

%mend;

%t();

%put &name;

Since the macro variable name is only declared global (or well, at a higher nesting level for embeded macros), the %let name=test; applies to the same macro variable already defined rather than creating a new local version.

/* Second example - good practice-declaring all local macro variables as local in case they existed in the global environment */

%let name=work.myds; /* implicit %global name x y; */

%let x=10;

%let y=10;

/* some sas statements */

%macro t();

%local x;

%let x=1;

%let y=1;

data &name;

x=&x;

%put x inside = &x;

%put y inside = &y;

run;

%mend;

%t();

%put x outside = &x;

%put y outside = &y;

/* Third example - passing a global macro variable content in a macro*/

%let name=work.myds; /* implicit %global name; */

%macro t(name=); /* implicit %local name; */

data &name; /* uses the local copy of name */

x=1;

%let name=INSIDE;

%put '&name' inside the macro=&name;

run;

%mend;

%t(name=&name); /* This effectively resolves &name as work.myds and stores this string in the LOCAL to t() macro variable name */

%put '&name' outside the macro=&name;

Anyway long story short, as people mentionned above, you did not need name as a global macro variable in the sample code you've provided. If you ever wish to emulate passing a macro variable by reference so that you can modify it inside a macro, you must declare it before the macro. However, should you declare a %local macro of the same name inside the macro, you will no longer have access to the global scope variable with the same name inside the macro and thus will no longer be able to modify it inside your macro. In most case scenario though, simply doing %t(name=&name); that is, passing the content of global macro variable name to the local macro variable name will achieve your desired results since you do not intend to modify the global macro variable.

Hope this helps.

Vincent

Super User
Posts: 5,071

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Vincent,

I'm not saying this is a good idea, but you can use CALL SYMPUTX as a workaround.  For example:

%macro test (name=);

   data _null_;

   call symputx ('name', "&name", 'Global');

   run;

   %put _user_;

%mend test;

%test (name=Vince)

Super Contributor
Posts: 339

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Indeed, there are a whole lot of ways around and each with their own advantages and disadvantages.

For instance, if you attempt to do %put &name; instead of %put _user_;, you will get an error because the macro processor recognizes that macro variable name was updated through the macro and thus segments the macro code, executes the data _null_; ... run; segment to update the macro variable only to find out that the call symputx cannot be processed while within the macro as you are trying to declare a %global name; in an environment where %local name; already exists.

%macro test (name=);

   data _null_;

   call symputx ('name', "&name", 'Global');

   run;

   %put &name;

%mend test;

%test (name=Vince);

There are other alternatives as well like passing the name of a global macro variable or  declaring a bunch of different %global inside the macro and documenting the macro that it will replace any such existing macro values if they already exist in the global environment.

E.g.

%let name=somelib.myds;

%let nameout=placeholder; /* statement not necessary, I just meant to depict that you can declare a variable as global from within a macro even when it was already declared as such. */

%macro t(namein=);

%global nameout;

%let nameout=work.%scan(&namein, 2);

data &nameout;

x=1;

run;

%mend;

%t(namein=&name);

%put &nameout; /* acts as a macro output parameter */

I'm still learning some of the twists of functioning with macros especially in terms of best practice but for some reason, I've always tried to stay away from using call symputx within a macro unless it was specifically to read data from a dataset as needed locally by the macro...and I think the first example explains why. However, I am more than welcome to read from more experienced users about this particular topic.

I typically prefer passing the name of a macro variable in the global scope if I want my macros arguments to be meaningful.

%let name=work.myds;

%macro t(namein=);

data &&&namein; /* Since I know that I am passing a macro variable name, in order to resolve its value I need too (&&) (&namein) resolved to -> (&) (name) -> work.myds; */

x=1;

run;

%mend;

%t(namein=name);

However, it can get confusing.

SAS macro language is actually a very fun brain tease since it is basically just a mass text parser and mimicing programming language functionalities like C sometimes requires a twist of mind.

Anyway,

Cheers!

Vincent

Super User
Posts: 5,071

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Vincent,

I agree with all the points you are making, but I am getting a different result in one regard.  I tried replacing %put _user_ with %put &name and had no difficulties doing that.  (running SAS 9.3 under Linux)

Good luck.

Super Contributor
Posts: 339

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Odd. Here's my log on a fresh new session SAS 9.2 under windows 32-bit

-------------------------------------------------

1    %macro test (name=);

2       data _null_;

3       call symputx ('name', "&name", 'Global');

4       run;

5       %put &name;

6    %mend test;

7    %test (name=Vince);

ERROR: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

NOTE: Argument 1 to function SYMPUTX at line 1 column 23 is invalid.

_ERROR_=1 _N_=1

NOTE: The SAS System stopped processing this step because of errors.

NOTE: DATA statement used (Total process time):

      real time           0.51 seconds

      cpu time            0.00 seconds

Vince

-------------------------------------------------

Would you mind testing the following code segment


%symdel name1;

%macro test (name=);

   data _null_;

   call symputx ('name1', "&name"||"2", 'Global');

   run;

   %put &name1;

%mend test;

%test (name=Vince);

I assume somehow either in 9.3 or under linux, the macro processor behaves slightly differently. If it returns something like

WARNING: Apparent symbolic reference NAME1 not resolved.

I would guess that under linux, the macro processor does not attempt to execute part of the macro and thus the new global macro variable only exists once the macro was exited and that you did not get an error with the above example because it used the local macro variable name.

If however it does resolve to Vince2, then I guess between 9.2 and 9.3 they improved the macro processor on such ambiguities.

If the former case applies, then it still means that using the call symputx syntax does not allow you to use the new global macro content within your macro whereas some of the alternatives I've examplified above would.

Thanks,

Vincent

Super User
Posts: 5,071

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Minor tweaks to the code, but no problems encountered:

       
15         %symdel name1;
WARNING: Attempt to delete macro variable NAME1 failed. Variable not found.
16        
17         %macro test (name=);
18            data _null_;
19              call symputx('name1', "&name.2", 'G');
20            run;
21            %put INSIDE:  &name1;
22         %mend test;
23         %test (name=Vince)

2                                                          The SAS System                             11:40 Wednesday, July 31, 2013

NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds
     

INSIDE:  Vince2
24         %put OUTSIDE:  &name1;
OUTSIDE:  Vince2

It shouldn't make a difference but just for the record, I'm running in batch mode.

PROC Star
Posts: 1,226

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

I like that the %GLOBAL statement produces an error in this context:

 
1          %macro mac1(dummy);
2            %local x;
3            %global x;
4          %mend;
5          
6          %mac1()
ERROR: Attempt to %GLOBAL a name (X) which exists in a local environment.

There would be no benefit to allowing the %GLOBAL statement to create a global macro var in this setting, since you could not write to or read from it.  Every reference to &x inside the macro will use the local version.

I find it odd that SYMPUTX lets you do this (at least in 9.3 on Win):

1          %macro mac2(dummy);
2            %local x;
3            %let x=000;
4            data _null_;
5              call symputx('x',"999","Global");
6            run;
7            %put _user_;
8          %mend;
9          
10         %mac2()

MAC2 X 000
MAC2 DUMMY 
GLOBAL X 999

This means that within a macro, you can write to a global macro variable with the same name as a local macro variable.  But you still can't reference the global variable (ie resolve it).

Unless/until SAS gives us the ability to specify the scope of the macro variable when it is referenced, I don't see much benefit to this artifact(?) of SYMPUTX.

But then, I don't think there is much benefit to macros being able to write to global macro vars in general...

Super User
Posts: 5,071

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Quentin,

In complex situations, you might want to specify the scope of a macro variable when it is referenced.  Picture macros calling macros, with the entire system written by a team of programmers.  When you retrieve &NAME, you might want to guarantee that it is the value coming from the global symbol table.  You can't do that by utilizing the macro variable reference, but you can do it.  SASHELP.VMACRO contains enough information to let you retrieve &NAME, with scope='GLOBAL'.

Respected Advisor
Posts: 3,777

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

You can nowadays create a global that has the same name as a parameter(local) macro variable using symputX.  I'm not sure how useful it is.

23         data &name;
24            x=1;
25            if _n_ eq 1 then call symputX('name',"&name",'G');
26            run;
27        
28         %mend;
29        
30         %t(name=p);

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

31         %put NOTE: &=name;
NOTE: NAME=p

Regular Contributor
Posts: 198

Re: Attempt to %GLOBAL a name (NAME) which exists in a local environment.

Tell us what you think you want to do.

What you want to do is not possible with the macro variables you have described.

What you said:

> I want NAME to be global macro variable, how do I fix

What we extrapolate from your macro:

%macro t(name=);

%*global &name;

data &name;

x=1;

run;

%mend;


Consider:


%Let data_set_name = P;


%t(name=&data_set_name)


the data set created by the macro is named P;


proc sql; describe table &data_set_name;

          quit;


btw:

This works, but is considered Bad Form:


%Let data = MyData;

%MyMacro(data = &data)


I recommend this as readable:

%Let In_Data = MyData;

%MyMacro(data = &In_Data)


Ron Fehd  readability maven

Ask a Question
Discussion stats
  • 15 replies
  • 3793 views
  • 0 likes
  • 9 in conversation