I have encountered another expansion issue and have narrowed it down to this simple test case of a macro that does nothing, well at least it tries to do nothing, but is not all that successful.
If I declare this macro as:
\newcommand*{\DoNothingA}[1]{#1}%
This works great, no surprises there.
However, if this macros is defined as follows:
\newcommand*{\DoNothingB}[1]{%
\def\Temp{#1}%
\Temp%
}%
then, this has problems where there is an attempt to use this in an \edef
.
Output:
Note that the MWE below yields a compile error for the last step, so you need to hit <RETURN>
to skip past it to see the output (or comment out the last \edef
, but then this won't show Question 2 below).
The MWE below yields the following:
The last line being especially curious as it produced foo 4
, and was the result of:
\edef\NewTemp{\DoNothingA{foo 5}}
\NewTemp
\color{red}% Highlight problem output
\edef\NewTemp{\DoNothingB{foo 6}}
\NewTemp
\color{black}
~-- used \verb|\edef| to store output of macro
Questions:
- What is the explanation of the expansion issue here?
- How does the final output become
foo 4
(foo 5
would have been easier to accept), if we skip past the compile error? - Since David Carlisle has been quoted as saying that "a few extra
\expandafter
cure all known ills" :-), what is the sequence of\expandafter
s needed in the last\edef
to use macro\DoNothinB
as defined here. - How can I adjust the
\DoNothingB{}
macro to use a temp variable and still work in the three cases in the MWE (preferrably without having to use any\expandafter
trickery.
Code:
\documentclass{article}
\usepackage{xcolor}% To highlight problem output
\newcommand*{\DoNothingA}[1]{#1}% Does everything I want in all cases
\newcommand*{\DoNothingB}[1]{% Works mostly, except if used in a \edef
\def\Temp{#1}%
\Temp%
}%
\begin{document}
% These two work great.
\DoNothingA{foo 1}
\DoNothingB{foo 2}
-- used output of macro directly
% These two also work great.
\def\NewTemp{\DoNothingA{foo 3}}
\NewTemp
\def\NewTemp{\DoNothingB{foo 4}}
\NewTemp
~-- used \verb|\def| to store output of macro
\edef\NewTemp{\DoNothingA{foo 5}}
\NewTemp
\color{red}% Highlight problem output
\edef\NewTemp{\DoNothingB{foo 6}}
%\show\NewTemp% Excellent suggestion by David Carlisle yields: \def foo 4{foo 6}foo 4
\NewTemp
\color{black}
~-- used \verb|\edef| to store output of macro
% Why is the last output "foo 4" (after ignoring error)??
\end{document}
Best Answer
Short answer: You can't use assignments like
\def
or\edef
inside expandable contexts like\edef
because they are not expandable!Long answer:
An
\edef
expands its content until only non-expandable tokens are left (note that text is not expandable).\def
is an assignment and all assignments must be executed and are therefore not expandable. Your\DoNothingB
actually does something: it defines\Temp
!Let me explain what happens on the following example:
then the
\edef
tries to expand first\def
which is not expandable and therefore stays at it is, then\temp
which is expanded tobefore
and then the rest: the tokens{
,s
,o
, ...,g
, and}
, which are all not expandable either. Therefore you get\NewTemp
as\def before{something}
which will instruct TeX, once\NewTemp
is expanded, i.e. used, to defineb
with the parameter textefore
and the replacement textsomething
. Becauseb
is immutable (as long it is not declared an active character) you will get an error here.In your example
\Temp
has been define tofoo 4
in your fourth example, where you use\DoNothingB
outside of an\edef
. In the next usage inside of an\edef
(\edef\NewTemp{\DoNothingB{foo 6}}
) you get\def foo4{foo 6}foo4
for\NewTemp
because both\Temp
usage are expanded right then, which leads to the above mentioned error. The\def foo4{foo 6}
is discarded as part of the error handling and thenfoo4
is typeset. If\Temp
would be defined differently before the output would be accordantly.A few
\expandafter
s don't help you here because the order is not the issue here and\edef
expands anyway everything continuously until only non-expandable tokens remain.However, you might want to add some
\noexpand
direct before the\Temp
macros inside\DoNothingB
to protect them from premature expansion in the first\edef
. This now doesn't work inside a\def
. Normally you can use\protect
with LaTeX's\protected@edef
instead. Depending on the context\protect
is either\relax
(outside\protected@edef
) or\@unexpandable@protect
(inside\protected@edef
) which is similar to\noexpand
. For usage with\def
you need to test this mode by yourself and add a\noexpand
manually if insideedef
.The following code does this. Note that you need to replace all
\edef
s with\protected@edef
for this to work. The normal\edef
and other expanding contexts like\message
or\write
will not work.