GeneralMacro: name scope?

LaTeX specific issues not fitting into one of the other forums of this category.
Post Reply
arobase
Posts: 33
Joined: Sat Apr 14, 2012 7:46 pm

Macro: name scope?

Post by arobase »

The behavior of the code below for each variant of baz is as I'd expect. The one that is `not OK', however, expresses what I'm aiming for: \foo is accesible within the definition of \baz after \endgroup, but is otherwise `hidden' (in particular, it does not clash with \def\foo{Oops} defined after \endgroup).

Code: Select all

\begingroup
	\def\foo{bar}
%	\xdef\baz{\foo} % OK
%	\gdef\baz{\def\foo{bar}\foo}%OK
	\gdef\baz{\foo}	% not OK
\endgroup

\begin{document}

\baz 

%\def\foo{Oops}

\end{document}
I could remove \begingroup ... \endgroup altogether and replace \def\foo by \gdef\SCOPENAMEfoo and use this in the definition of \baz. I'm hoping there is a better/neater suggestion. How are packages written in this respect?

Recommended reading 2024:

LaTeXguide.org • LaTeX-Cookbook.net • TikZ.org

NEW: TikZ book now 40% off at Amazon.com for a short time.

User avatar
nlct
Posts: 276
Joined: Thu Nov 06, 2008 11:15 am

Macro: name scope?

Post by nlct »

Instead of

Code: Select all

\gdef\baz{\foo}
try

Code: Select all

\global\let\baz\foo
Regards
Nicola Talbot
User avatar
cgnieder
Site Moderator
Posts: 2000
Joined: Sat Apr 16, 2011 7:27 pm

Macro: name scope?

Post by cgnieder »

I'm not clear on what you want to achieve... you want \baz have the definition of \foo that it has inside the group but want \foo be undefined outside the group? One of your versions works:

Code: Select all

\begingroup
  \def\foo{bar}
  % \xdef is equal to \global\edef
  % expands \foo. the definition of \baz is therefore "bar"
  \xdef\baz{\foo}
\endgroup
\baz %\foo
You can also use something like this:

Code: Select all

\begingroup
  \def\foo{bar}
  \edef\@tempa{% use a temporary macro and close the group while defining it:
\endgroup\def\noexpand\baz{\foo}}\@tempa
\baz %\@tempa \foo
\makeatother
A third possibility would be a chain of \expandafters:

Code: Select all

\begingroup
  \def\foo{bar}
  \expandafter
\endgroup\expandafter\def\expandafter\baz\expandafter{\foo}‌​
\baz %\foo
Regards
site moderator & package author
arobase
Posts: 33
Joined: Sat Apr 14, 2012 7:46 pm

Macro: name scope?

Post by arobase »

The solution with \@temp seems to work, but I have to wrap my mind around it. The one with \global\let has not worked. The one with multiple expandafter seems to emulate \xdef, doesn't it?

Meanwhile let me rephrase my goal.

Let's say that \gdef\baz (not \xdef\baz) is the public interface of a package called baz. \foo is part of the overhead: it is meant to be used within \gdef\baz, not the user of the package.

Here's the dilemma: if I define \foo using \gdef, the package works, but \foo becomes part of the public interface, which is unwanted. If I define it using \def the package does not work.
User avatar
cgnieder
Site Moderator
Posts: 2000
Joined: Sat Apr 16, 2011 7:27 pm

Macro: name scope?

Post by cgnieder »

I still don't understand. Why do you want that \baz contains unexpanded content that is not defined? You either have to expand \foo in the process or define both macros either globally or not inside the group.

The \expandafter method does not exactly emulate the \xdef version. In the first case \foo is expanded once, in the second the contents of \xdef's second argument are expanded until there are only unexpandable tokens left.

Compare these:

Code: Select all

\documentclass{article}
\begin{document}
\def\a{Aa}
\def\b{Bb}
\def\foo{\a\b}

\expandafter\def\expandafter\bar\expandafter{\foo}
\show\bar
% > \bar=macro:
% ->\a \b .

\edef\baz{\foo}
\show\baz
% > \baz=macro:
% ->AaBb.

\end{document}
Last edited by cgnieder on Wed May 30, 2012 3:41 pm, edited 2 times in total.
site moderator & package author
User avatar
cgnieder
Site Moderator
Posts: 2000
Joined: Sat Apr 16, 2011 7:27 pm

Macro: name scope?

Post by cgnieder »

BTW: it is common practice to define internal package macros that are not meant to be used by users using @ in their names, something like

Code: Select all

% internal macros:
\def\mypackage@foo{<some cool definition>}
\def\mypackage@bar#1{<another cool definition using #1>}
% user macros:
\let\foo\mypackage@foo
\def\bar#1{\mypackage@bar{#1}}
site moderator & package author
arobase
Posts: 33
Joined: Sat Apr 14, 2012 7:46 pm

Macro: name scope?

Post by arobase »

cgnieder wrote:I still don't understand. Why do you want that \baz contains unexpanded contents that is not defined?
I don't see how saying that \foo is part of the overhead of \baz conveys that. In any case, I didn't mean it, and I have acknowledged this constraint:
cgnieder wrote:You either have to expand \foo in the process or define both macros either globally or not inside the group.
The solution with \@temp is not going to work if \foo takes an argument. Thus far, I only see one solution, and that's to do \gdef\PACKAGEOVERHEADfoo where PACKAGEOVERHEAD is chosen to minize the chance that \PACKAGEOVERHEADfoo clashes with a macro defined outside the package.
User avatar
cgnieder
Site Moderator
Posts: 2000
Joined: Sat Apr 16, 2011 7:27 pm

Macro: name scope?

Post by cgnieder »

arobase wrote:I don't see how saying that \foo is part of the overhead of \baz conveys that.
I may well have misunderstood you. English is not my native language.

arobase wrote:The solution with \@temp is not going to work if \foo takes an argument.
Well, you have to adapt, of course. It really depends. This works:

Code: Select all

\begingroup
  \def\foo#1{bar: #1!}
  \edef\@tempa{% use a temporary macro and close the group while defining it:
\endgroup\def\noexpand\baz##1{\foo{##1}}}\@tempa
\makeatother
\baz{X} %\@tempa \foo
Regards
site moderator & package author
Post Reply