Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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?

share|improve this question

2 Answers 2

up vote 18 down vote accepted

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.

share|improve this answer
1  
The key point here is to avoid defining \directlua at all outside of the test. This approach keeps the hash table clean. –  Joseph Wright Jul 29 '11 at 8:47
    
@Joseph: You're right, I started thinking of saying it and forgot doing so. –  egreg Jul 29 '11 at 8:49
1  
@Joseph: I think I said something along those lines on comp.text.tex, and got corrected. Control sequences are put in the hash table as soon as they are seen in any kind of token list, and nothing is ever removed from the hash table. The eq table, however, is indeed changed by assignments, and with this contruction is left unchanged. I think that the eTeX manual mentions that \ifdefined and \ifcsname don't add to the hash table. --- all this said, I know very little on that topic. –  Bruno Le Floch Jul 30 '11 at 1:55
2  
@Bruno Yes, with \csname...\endcsname the hash table is always touched. The point is just not to have \directlua in the equivalence table, which is the most that can be done in pure TeX. –  egreg Jul 30 '11 at 8:12
    
The TeX FAQ offers the simpler {\expandafter}\expandafter\ifx \csname directlua\endcsname\relax ..., which if I understand properly what's going on, should have the same effect as the longer code in the question...? –  dubiousjim Aug 19 at 12:21

The reason here is that \csname ...\endcsname will define ... as a macro equal to \relax should it not already exists. This feature is used with \ifx which compares it to \relax. This test is true if ... wasn't defined before (or was \let to \relax).

However, it isn't good practice to define macros even to \relax just for testing their existence. e-TeX provides \ifcsname ...\endcsname for this. Without e-TeX the a group can be used together with \expandafter to process both the \csname and the \ifx inside it to keep the macro definition local.

Note that when an \if... is true TeX simply goes on with processing the following tokens. It remembers looking for the closing \fi which is simply removed or an \else branch which should be skipped. If the test is false everything till \else is immediately skipped and TeX again remembers to look for an closing \fi. Therefore all the \expandafter trickery works very well. The \ifx is expanded and TeX already has chosen which branch it will execute. Then the \endgroup is insert and that branch is executed.

The benefit for wrapping the whole expression inside a groups is very clear: the actual content can define/change local settings!

Note that that in TeX if-statements and groups are independent (which is not the case in almost any other programming language). You can therefore also write the following to keep the \csname ...\endcsname statement local:

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

Only one \endgroup is ever executed here.

share|improve this answer
3  
There's also \ifdefined, so \ifdefined\directlua is more direct. –  egreg Jul 29 '11 at 9:05
1  
Missing \expandafter ? –  Taco Hoekwater Jul 29 '11 at 11:33
    
@Martin Scharrer: it is: \expandafter\ifx\csname directlua\endcsname\relax (or \ifx\directlua\undefined). –  Bruno Le Floch Jul 30 '11 at 1:57
    
@Bruno: Yes of course. I was locking at the \begingroup instead :-) –  Martin Scharrer Jul 30 '11 at 9:36

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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