In the definition of \if on page 209 in TeXbook it is said:

unless the current equivalent of that control sequence has been \let equal to a non-active character token

Do I understand it correctly that, if we use


then here, according to the definition, "the current equivalent of that control sequence" is \x and "non-active character token" is <y>?

Why does the definition use the term "current equivalent of that control sequence" instead of "that control sequence (which may be an active character)", which would be more clear?

Also, why does the definition bother with saying "non-active"? Because if the character token <y> was active, it would have been expanded (recall that \if expands all expandable tokens).

Also, the definition says "character token". But there also can be a "space token". So, to avoid ambiguity, wouldn't it be better to say "unless the <token> in a \let is a single character - i.e., if it is a (character code, category code) pair"?

  • But we don't know that \x is <y>. \x could be anything, including another control sequence which might be \let to a non-active character token. But I'm not saying that it makes sense on that interpretation. I was just thinking.
    – cfr
    Oct 25, 2023 at 3:27
  • Possible duplicate: tex.stackexchange.com/questions/386480/….
    – cfr
    Oct 25, 2023 at 3:29
  • I'm going to tidy up a little.
    – cfr
    Oct 25, 2023 at 3:33
  • "current equivalent" being the equivalent at use time, it makes sense to highlight this, and no, the current equivalent is not \x but the \meaning of \x (which after \let\x y is the letter y). If <y> was active you'd let \x to the \meaning of <y>, which in turn could be anything, and if that \meaning is expandable \x will be expanded. A space token has character code (32), so one could consider it a character token (with some interesting behaviour).
    – Skillmon
    Oct 25, 2023 at 6:31

4 Answers 4


The \if behavior can be clearly described by its equivalence classes. There is one equivalence class with all control sequences which are not equal (by \let) to a character token. Moreover, each character code has its own equivalence class with all tokens with the same character code (i.e. the category code is irrelevant) and with all control sequences which are equal to these tokens (by \let).

Try this:

\def Z{}
\if \noexpand Z\string Z YES\else NO\fi
\if \noexpand Z\z YES\else NO\fi 

both cases gives YES. First case: there are two tokens Z, one with catcode 13 and second with catcode 12. Second case: token Z with catcode 13 and a control sequence equal to a token Z with catcode 11.

There is a special rule of \let behavior: if \let\cs=A is done, where A is an active character, then \cs isn't equal to this active character but to its current meaning. For example, if A is a macro then \cs is this macro after \let\cs=A. Such a \cs is element of the first equivalence class given by \if (i.e. \if\noexpand\cs\hbox gives true). On the other hand, if the meaning of the active A is a non-active character (by previous \let A=c) and \let\cs=A is done, then \cs belongs to the equivalence class where c is. Moreover, the active A itself is not equal to \string A in this case, but to its meaning c.

Try this:

\def A{macro}
\if\noexpand\cs\hbox True\else False\fi     % True
\if\noexpand A\hbox True\else False\fi      % False
\if\noexpand A\string A True\else False\fi  % True

\let A=c
\if\noexpand\cs\hbox True\else False\fi % False
\if\noexpand\cs c True\else False\fi    % True

\if\noexpand A\string A True\else False \fi % False
\if\noexpand Ac True\else False\fi          % True
  • Could you please rewrite your examples without using \detokenize. It does not work in Knuth's TeX. Oct 26, 2023 at 7:35
  • Who needs only Knuth's original TeX today? But OK, I rewrote my examples.
    – wipet
    Oct 26, 2023 at 12:11
  • Just guessing as to the motivation for @IgorLiferenko 's request, but I don't think the point is which system is being used. It's rather pedagogical. If you're trying to work through the TeX Book, it's probably useful to have an answer in those terms.
    – cfr
    Oct 26, 2023 at 14:47
  • Please add to your answer explanation why this example gives FALSE: \let\a=a \if\a{a}\message{TRUE}\else\message{FALSE}\fi \bye Oct 27, 2023 at 1:14
  • Because a is not equal {. And the paired } is skipped, so you don't get error message due to bad syntax.
    – wipet
    Oct 27, 2023 at 5:10

Why does the definition use the term "current equivalent of that control sequence" instead of "that control sequence (which may be an active character)", which would be more clear?

That would mean something completely different.

\if is intended to compare character tokens, the following tokens are expanded and the first two non expandable tokens a are comared. (The tokens may actually be expandable if expansion was suppressed with \noexpand, or e-tex \unexpanded)

In general any non-character token compares like \relax and is equal to other non character tokens and not equal to all character tokens.

The exception ts implicit character tokens which are control tokens that have been \let to a character token.

so if you go


then \implicitx is \if equal to x

This is what the TeX Book is refering to in the line you quote, the control sequence \implicitx has (at the point of the \if test) been \let to the non active character token x.

If instead you had done


then \notimplicittilde is not an implicit character token, it is simply a normal macro csname with definition being the same as that of ~

This plain tex file





> \implicitx=the letter x.
l.3 \show\implicitx
> \notimplicittilde=macro:
->\penalty \@M \ .
l.6 \show\notimplicittilde

Where \implicitx has \show meaning the letter which tells you it acts as a character for \if and would act as a catcode 11 (letter) token for \ifcat

Conversely \notimplicittilde has \show meaning macro: which tells you it it is an expandable macro so will not be \if equal to anything. (\if\notimplicittide would actually be \if\penalty\@M which is false)


Let's look at the full quotation:

TeX will expand macros following \if until two unexpandable tokens are found. If either token is a control sequence, TeX considers it to have character code 256 and category code 16, unless the current equivalent of that control sequence has been \let equal to a non-active character token. In this way, each token specifies a (character~code, category code) pair.

In my opinion it would be better if 256 and 16 were −1; what Knuth wants to say is that the character and category codes cannot match those of a “real” character. In the source code of TeX the actual values 256 and 16 are not used. Note also that “is a control sequence” must be interpreted as “is a control sequence or active character”.

Now it makes more sense, doesn't it? At the point the TeXbook is describing, the expansion process has ended and the input stream begins with two unexpandable tokens, let's call them A and B.

If neither is a control sequence, they're characters and the comparison is done based on character codes. What the TeXbook is saying is that if A (or B) is a control sequence \cs (but it might be an active character), then two cases are possible:

  1. \cs has the same meaning as an unexpandable primitive (such as \hbox) or a \chardef, \mathchardef, \countdef, \dimendef, \skipdef, \muskipdef, \toksdef token;

  2. the current meaning of \cs has been defined by \let\cs=<char> where <char> is a nonactive character token.

Case 2 includes also something like \let\x=a followed by \let\cs=\x, which would be the same as \let\cs=a.

Why not a third case, when the current meaning of \cs has been obtained by \let\cs=~, with ~ an active character? Because this cannot happen.

Remember that \cs is unexpandable to begin with and that \let\cs=~ assigns \cs the same meaning as ~ (at the time of the assignment).

  • It could be pointed out that \cs and/or \x can be active characters. Oct 25, 2023 at 8:40
  • @IgorLiferenko I posted the wrong text (changed machine) and indeed I had realized that what you mention should be in the answer.
    – egreg
    Oct 25, 2023 at 9:03

The simple test to tell if \ThatControlSequence or <ThatActiveCharacter>is assigned (256,16) pair in an \if test is as follows.




\show <ThatActiveCharacter>

If the output will say =, then (256,16) is assigned (this is not certain - more thorough research is needed).

  • I think this is misleading, \let\wibble\relax\show\wibble makes \wibble=\relax but this acts as a non expandable non character token, whch is I think what you mean by 256,16 (256 of course not the right number in unicode tex systems) Oct 25, 2023 at 8:40
  • @DavidCarlisle so, the simple criteria would be "if the letter ... is printed, then pair ([0-255],[0-15]) is assigned. Otherwise pair (256,16) is assigned." Right? Oct 25, 2023 at 8:43
  • no, letter means that the catcode is 11 other catcodes have other names. catccode 12 is the character 4 is alignment tab character etc. Oct 25, 2023 at 8:46
  • more or less I think if you get = it's a non expandable non character token, but I'd need to think about that. Oct 25, 2023 at 8:48
  • @DavidCarlisle also, for space blank space is printed Oct 25, 2023 at 9:24

You must log in to answer this question.

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