9

A few days ago I tried to point out to Will Robertson one of the pitfalls of eTeX’s \ifcsname and \ifdefined but he didn’t seem to get the message. Please am I right in saying that the following beckons us to be cautious when using\ifcsname and \ifdefined, since they return 'true' when a command is made \relax? Tests like etoolbox's \ifdef and \ifcsdef should be preferred when the aim is to test if a command is equivalent to \undefined.

With the hope of later doing, e.g., \expandafter<cmd>\x, I defined

\edef\x{\unexpanded\expandafter{\csname test@\romannumeral\currentgrouplevel\endcsname}}

After some intervening code, I then did

\ifcsname test@\romannumeral\currentgrouplevel\endcsname
  \def\y{Yes}
\else
  \def\y{No}
\fi
\show\y

On the face of it, there was no apparent relation between defining \x and the later test. I first wondered where test@\romannumeral\currentgrouplevel was defined. It took me some time to locate the problem.

6
  • 6
    Two rather separate issues here: could you post the \edef\x{\ifnum0=0\else\fi} separately, and perhaps give this one a clearer name?
    – Joseph Wright
    May 26, 2012 at 8:56
  • 1
    I agree with @Joseph on both accounts. Also, the second question is quite related to your existing question How to vanish \relax inserted by TeX itself. TeX inserts an implicit \relax because it finds \else in number reading mode which needs to be terminated then. May 26, 2012 at 9:10
  • @MartinScharrer: Thank you for the reference. The question I should actually be asking is why is it that with \edef\x{\ifnum0=0\else\fi} being \relax and \def\y{\relax}, we see that \ifx\x\y is false? Are there two types of \relax?
    – Ahmed Musa
    May 26, 2012 at 10:35
  • 1
    @AhmedMusa I can transfer the second part of my answer, if you make a new question.
    – egreg
    May 26, 2012 at 10:47
  • 3
    It beckons us to be careful with \csname ... \endcsname because that does define the cs (which is precisely why the eTeX developers added the new primitives to allow for an expandable test that doesn't define the cs). So perhaps you should reword the start of your question. May 26, 2012 at 15:12

3 Answers 3

12

It's not misleading because it follows the precisely laid out rules.

  1. When \csname is expanded, TeX looks for the matching \endcsname with expansion; then TeX forms a control sequence token from the character tokens (irrespective of category code) it has found or issues an error if some non character token is discovered.

  2. After the formation of the token, TeX looks up in its memory to find if that token has a meaning. If it hasn't, that token is made equivalent to \relax with a local assignment.

  3. \edef expands the tokens it finds between the braces that delimit the replacement text before storing the resulting <balanced text> as the replacement text.

Thus your \edef\x{\csname totallyundefined\endcsname} is equivalent to

\let\totallyundefined\relax
\def\x{\totallyundefined}

On the contrary, the \ifdefined and \ifcsname tests (by precise choice of e-TeX's developers) never perform the assignment to \relax of an undefined token.

The \relax assignment is performed in many other cases as a precaution against premature expansions. Two cases mentioned in the TeXbook are

\chardef\cs=10\cs
\font\cs=name\cs

where \cs is temporarily made equivalent to \relax in order to stop expansion when the assignment is performed. In such cases it's really temporary, as \cs will immediately be assigned a new meaning; in the case of \csname it isn't. Where's the problem? If \cs had a previous definition, without the temporary assignment to \relax, TeX would expand \cs searching for a number in the first case or the end of the file name in the second. Even worse, if \cs was undefined, an error would be raised, because TeX hasn't yet assigned to \cs the new meaning. Of course, using such a syntax is discouraged.

It may be unfortunate that e-TeX uses \ifcsname...\endcsname with the same rules of \csname...\endcsname for the formation of the token to be examined, but with the fundamental difference that the equivalence to \relax is not performed, but that's what it is.


In the case you're presenting, that is

\edef\x{\unexpanded\expandafter{\csname test@\romannumeral\currentgrouplevel\endcsname}}

you might do it indirectly:

\edef\x{\noexpand\csname @test\romannumeral\currentgrouplevel\endcsname}

so that the \csname won't be executed. Of course \x would need two expansions steps:

\expandafter\expandafter\expandafter\cs\x
3
  • \endcsname is unexpandable, so you don't need that second \noexpand in your last example. May 26, 2012 at 22:25
  • @egreg: \chardef\cs=10\cs gives {\char"A}\everypar->\@nodocument outside \document. This syntax should be qualified.
    – Ahmed Musa
    May 27, 2012 at 0:53
  • @AhmedMusa This is a Plain TeX example. Of course you get an error if use that in the preamble of a LaTeX document, because you're trying to typeset a character.
    – egreg
    May 27, 2012 at 8:29
9

Both \ifcsname and \ifdefined test if the control sequence is defined at all, this includes control sequences which are equal to \relax. Earlier tests use \expandafter\ifx\csname abc\endcsname\relax which uses the feature of \csname to define undefined control sequences automatically to be equal to relax. You are right that mixing these two ways to test is dangerous, because any \ifx\csname test or other \csname on the same name will change the outcome of the other two tests on the same control sequence, because it is now defined equal to \relax. Both \ifcsname and \ifdefined intensionally do not define the control sequence to \relax and should actually be preferred over \ifx\csname, but you might only want them to use with control sequences your package etc. defines itself where no other code tested it using a different test.

BTW: You can use grouping like: \begingroup\expandafter\ifx\csname abc\endcsname\relax\endgroup ..\else\endgroup .. \fi, to keep the \csname definition local, but the LaTeX core macro \@ifundefined doesn't do that.

2
  • 1
    The problem with Heiko-style \begingroup\expandafter\expandafter\expandafter\endgroup \expandafter\ifx\csname x\endcsname ... is that it isn't expandable.
    – Ahmed Musa
    May 26, 2012 at 10:43
  • 1
    @AhmedMusa: Yes, I know. I was thinking mentioning that, but the question didn't mentioned expandability so I didn't. May 26, 2012 at 10:44
5

Not sure if the second part of the question will be migrated but here is an explanation in any case.

The expansion of \ifnum...\fi is empty if the test is false, \relax otherwise, thus the meaning of

   `\edef\x{\ifnum0=0\fi}`

is

   `macro:->\relax`

Try this test for fun:

    {\def\relax{0}\xdef\foo{\ifnum0=0\fi}}

This puts \relax in \foo. Although tricky, this token is not the same token as the one that follows the \def, since otherwise 0\fi would expand to 0\relax\fi then 00\fi, then 00\relax\fi without end (and without overflow).

This behaviour is not an oddity, but a mind boggling fact!

6
  • Sorry, but the explanation is incorrect: with \edef\x{\ifnum0=0 \fi} the expansion of \x is empty.
    – egreg
    May 26, 2012 at 9:46
  • @egreg Not on my computer! Try \edef\x{\ifnum0=0\fi} \tt \meaning\x \bye
    – yannisl
    May 26, 2012 at 9:54
  • 1
    @egreg True if you leave a space or an A for example but I don't see why you say the explanation is incorrect.
    – yannisl
    May 26, 2012 at 10:04
  • 1
    The correct explanation is "unfinished conditional" and "frozen \relax" that I learnt from David Kastrup.
    – egreg
    May 26, 2012 at 10:10
  • 1
    You should cut-and-paste this answer to Normal \relax vs. frozen \relax
    – lockstep
    May 26, 2012 at 11:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .