5

I defined two macros \newfoo{#1}{#2} and \foo{#1}. The first parameter is used as a key and must be a valid LaTeX macro name (without the backslash). The second parameter is a short piece of text. Using \newfoo a key is associated with its text and using \foo the text is supposed to be printed, given the previously defined key.

For those familiar with the glossary/-ies package, the commands can best be depicted as a poor-man's version of those, but I need them in other context.

Internally, \newfoo{#1}{#2} defines a macro \foo@#1 and \foo{#1} expands to \foo@#1. The MWE below works. But I would have expected to get a compile error, if \foo is called for a key that has not been defined previously, because \foo@#1 does not exist. However, compilation proceeds silently and simply expands a non-existing \foo@#1 into nothing. This behavior is undesired, because misspelled keys should be noted.

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage[utf8]{inputenc}

\makeatletter

\newcommand*\newfoo[2]{%
\expandafter\newcommand\csname foo@#1\endcsname{#2}%
}

\newcommand*\foo[1]{\csname foo@#1\endcsname}

\makeatother

\newfoo{a}{1st letter of alphabet}
\newfoo{b}{2nd letter of alphabet}

\begin{document}

\verb#\foo{a}# is expected to expand to ``\foo{a}''

\verb#\foo{b}# is expected to expand to ``\foo{b}''

\verb#\foo{c}# has not been defined by \verb#\newfoo{c}{...}# and should raise a compile error

\end{document}
4

The solution is

\newcommand*\foo[1]{%
  \ifcsname foo@#1\endcsname
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\csname foo@#1\endcsname}%
  {\ERROR}%
}

or, using a kernel construction

\newcommand*{\foo}[1]{%
  \@ifundefined{foo@#1}{\ERROR}{\csname foo@#1\endcsname}%
}
7

It is a "feature" of \csname that previously undefined commands are defined as if \let to \relax if accessed via \csname ... \endcsname. Usually this is unwanted behaviour but it is used in a few places in LaTeX as it is the only way in classic TeX to make a definition in an expansion-only context and so can be used to flag certain things. (It is used for example to trap problematic characters that are encountered during writing a file in filecontents.)

1

To complete a tiny bit David Carlisle's excellent answer, please note that, had you used \foo{c} in your example, it would not have expanded to “nothing”, because \relax really isn't nothing. If you try to expand \relax, you get \relax because it is unexpandable.1 But \relax can stop things that a macro expanding to nothing wouldn't stop, such as:

  • the expansion process when TeX is reading an 〈integer constant〉, 〈octal constant〉, 〈hexadecimal constant〉 or a 〈glue〉 in the form of 〈dimen〉〈stretch〉〈shrink〉;
  • the expansion process when TeX is looking for \noalign or \omit at the beginning of an alignment (tabular, align...) or at the end of a row (even several space tokens wouldn't stop the search process in this case, but \relax does, and this can prevent \rowcolor from working in certain situations—see towards the end of this message for instance);
  • probably many other things.

So, the \relax command doesn't do much2 when TeX gets to finally digest it deep inside its stomach; but at other levels, it can have quite noticeable effects.

In your example, you can see for yourself that the expansion of \foo@c is \relax and not an empty expansion by doing two expansion steps before using \show: the first step expands \foo (which grabs {c}) to \csname foo@c\endcsname and the second step expands \csname (which grabs foo@c\endcsname) to \foo@c:

\documentclass{article}

\makeatletter
\newcommand*\newfoo[2]{%
  \expandafter\newcommand\csname foo@#1\endcsname{#2}%
}

\newcommand*\foo[1]{\csname foo@#1\endcsname}
\makeatother

\begin{document}
\expandafter\expandafter\expandafter\show\foo{c}
\end{document}

which outputs to the terminal:

> \foo@c=\relax.
<recently read> \foo@c 

l.12 ...dafter\expandafter\expandafter\show\foo{c}

Of course, the following also works:

\foo{c}%
\makeatletter
\show\foo@c
\makeatother

but with the side effect of executing \relax (!), and it's so much less fun...


Footnotes

  1. However, note that if \relax were defined by \def\relax{\relax}, it would behave completely differently—\relax isn't a quark.

  2. It can still prevent ligatures: compare f\relax f\relax i with ffi.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.