I want to add one technical point that recently proved crucial to me: while \catcode
changes are famously fragile in that they cannot occur after tokenization (specifically, after the argument to a macro is read), \mathcode
changes can occur. For example, I had the following macro:
\def\genby#1{\langle#1\rangle}
to specify the generators of some thing in algebra, where #1
was to represent the generating set. If I had several sets of generators of course I wanted to use the mathematically correct notation \genby{S \cup T}
rather than the easier but less correct \genby{S, T}
. Unfortunately, I kept forgetting and rather than search-and-replace all the commas, I tried to redefine the command:
{\catcode`\,=\active \gdef,{\cup}}
\def\genby#{%
\langle\bgroup \aftergroup\rangle
\catcode`\,=\active
\let\next=
}
(using this trick to avoid tokenization)
and while this works okay in $\genby{S,T}$
(producing the equivalent of $\genby{S \cup T}$
), it failed entirely in the amsmath
construction
\begin{gather}
\genby{S,T}
\end{gather}
where it had no effect! I immediately recognized this as an instance of "amsmath
reads its argument twice" (which I first learned about in this question, which is not the only place it's come up on this site) and figured it was a tokenization issue, blocking the catcode change. So I tried \mathcode
instead (also, see egreg's comment):
{\catcode`\,=\active \gdef,{\cup}}
\def\genby#1{%
\langle\begingroup
\mathcode`\,="8000
#1
\endgroup\rangle
}
or, without using a global definition for the active comma, that could conflict with other packages,
\newcommand{\genby}[1]{%
\begingroup
\begingroup\lccode`\~=`\,
\lowercase{\endgroup\let~\cup}%
\mathcode`\,=\string"8000
\langle#1\rangle
\endgroup}
and this worked again, even in gather
.
This feature emphasizes an important theoretical point about math mode: when operating in it, TeX has an intermediate stage of interpretation, similar to tokenization, in which it builds a "math list" that is more than just the textual input ("code") but less than the typeset output ("horizontal list"). The interpretation of mathcodes, delcodes, and so on only occurs at the conversion from a tokenized input stream to a math list, and therefore changing one of them is valid after tokenization. (You still can't do anything once the math list is built, though.)