[Tex/LaTex] What does \begingroup\expandafter…\endgroup do

conditionalsexpansiongroupingmacros

I often see code of the following form in package implementations (this example is from the LaTeX3 sources):

\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname directlua\endcsname\relax
\else
  …
\fi

The first line, containing three \expandafters, confuses me. I can only follow this far:

  • \begingroup starts a group
  • The chain of \expandafters causes \csname directlua\endcsname to be converted to a control sequence
  • After this point, the state is that we're in a group and \expandafter\endgroup\ifx[directlua]\relax… remain to be examined by the macro processor ([directlua] denotes a control sequence)
  • Now the last \expandafter is processed and \ifx is expanded, then \endgroup ends the group. The TeXbook says this on the topic:

    When an \if… is expanded, TeX reads ahead as far as
    necessary to determine whether the condition is true or false; and if
    false, it skips ahead (keeping track of \if…\fi nesting) until
    finding the \else, \or, or \fi that ends the skipped text. Similarly,
    when \else, \or, or \fi is expanded, TeX reads to the end of any text
    that ought to be skipped. The “expansion” of a conditional is empty.

    This would suggest that the arguments to \if… are evaluated inside the group. But what about code inside the conditional's branches?

If the purpose of the code in question is indeed to evaluate the \if… inside a group, why is it better than just inserting the conditional between \begingroup and \endgroup?

Best Answer

Let's look step by step

\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname directlua\endcsname\relax
  A
\else
  B
\fi

This becomes

(\begingroup)\expandafter\endgroup
\ifx\directlua\relax
  A
\else
  B
\fi

The \begingroup has already been digested, so I leave it in parentheses just to remember a group has been opened. Another step, now, where we have to distinguish between cases.

Case 1: \directlua is not defined, so the token produced by\csname directlua\endcsname is equivalent to \relax.

(\begingroup)\endgroup A\else B\fi

Now \endgroup is digested and this removes the assignment of the meaning \relax to \directlua. A is examined, the expansion of \else B\fi is empty.

Case 2: \directlua is defined.

(\begingroup)\endgroup B\fi

Again \endgroup is digested, but does not restore anything. The expansion of \fi is empty.

Why not doing this inside a group? The key point is that at the end \directlua is not defined if it wasn't at the start of the process. The same would be true if the code is

\begingroup\expandafter\ifx\csname directlua\endcsname\relax A\else B\fi\endgroup

However the purpose of A and B is doing some assignments. In this case A would probably be \luatexfalse, after having said before \newif\ifluatex, and B would be \luatextrue. The triple \expandafter inside the group dispenses from a global assignment, following the good practice that assignments to a variable should be always global or always local (so long as it's possible). Of course in this case a global assignment would not be that important, in other cases it might have consequences on the save stack.

The suggested alternative

{\expandafter}\expandafter\ifx\csname directlua\endcsname\undefined
  A
\else
  B
\fi

(with \undefined, not \relax) is less attractive, because it relies on a certain token to be undefined. One could object that the code we're analyzing assumes \relax has its primitive meaning, but some assumptions need to be made.

If e-TeX can be assumed, the simpler test

\ifdefined\directlua
  A
\else
  B
\fi

is even fully expandable.

Related Question